Yii2 динамические имена таблиц для модели, несколько таблиц БД для одной модели
погуглив проблему динамических таблиц для модели в yii2, нашел интересный пост на эту тему https://habrahabr.ru/sandbox/89573/.
в кратце:
- Создаем статическое свойство модели
public static $_tableName; // название таблиц +
- Переписываем метод получения имени таблицы для работы модели с БД
public static function tableName() { return self::$_tableName; }
- Пишим метод, который установит имя таблицы
/** * Устанавливает имя таблицы * @return type */ public function SetTableName() { self::$_tableName = self::CheckYear() . '_checks'; if (Yii::$app->db->getTableSchema(self::$_tableName, true) === null && Yii::$app->request->isPost == true) { self::CreateTable(self::$_tableName); } return self::$_tableName; }
На этом этапе мой код уже отличается от примера. В приме на хабре, таблицы уже были созданны в БД, у меня же они создаются динамически на основании данных переданых от клиента.
В 3 этапе начинается самое интересное, во-первых я проверяю год, т.к. он ограничен настройками года. Нельзя создать год больше определенного значения. Затем проверяем имя таблицы в БД(есть ли такая?), и если нет создаем.
Обратите внимание на второй параметр тут: Yii::$app->db->getTableSchema(self::$_tableName, true)
- это очень важно, после первого запроса Yii2 кеширует результат, и в итоге проверка будет давать не правильное значение. Установив true мы сбрасываем КЕШ.
Метод создания таблицы не сложный
/**
* Создает таблицу в БД
* @param type $table_name
*/
public static function CreateTable($table_name) {
Yii::$app->db->createCommand()->createTable($table_name, [
'ID' => 'pk',
'UF_OPERATOR' => 'string',
'UF_CART_NUMBER' => 'int(18)',
'UF_SUM' => 'double',
'UF_KASSA' => 'int(18)',
'UF_SHOP' => 'int(18)',
'UF_SKIDKA' => 'double',
'UF_XML_ID' => 'string(50)',
'UF_DATE' => 'datetime',
'UF_TOVARS_DOP' => 'text',
])->execute();
// creates index for column `UF_XML_ID`
Yii::$app->db->createCommand()->createIndex(
'ix_perf_checks_1',
$table_name,
'UF_XML_ID',
true // Уникальный индекс
)->execute();
// creates index for column `UF_XML_ID`
Yii::$app->db->createCommand()->createIndex(
'ix_perf_checks_2',
$table_name,
'UF_CART_NUMBER',
false
)->execute();
}
НО, после всего этого у меня все-равно не работало, как надо. Таблица создавалась один раз... Долго копая гугл и исходники фреймворка нашел нужный, метод который надо переписать:
/**
* Переопределяем метод, чтобы сбросить КЕШ о инфе об таблицы
* @return type
* @throws InvalidConfigException
*/
public static function getTableSchema() {
$tableSchema = Yii::$app->db->getTableSchema(static::tableName(), true);
if ($tableSchema === null) {
throw new HttpException(422, 'Не найдено чеков за ' . self::$year . ' год', 3);
}
return $tableSchema;
}
Он проверяет саму таблицу в БД, при создании модели данных. И так, как yii2 кешировал данные и на первом запросе таблицы действительно не было в БД, yii2 падал с ошибкой....
Я знаю, что трудно излагаю свои мысли, я программист :-) Есть вопросы, спрашивайте в комментах, обязательно отвечу.
Напоследок полный код модели:
<?php
namespace app\modules\rest\modules\v1\models;
use yii\db\ActiveRecord;
use yii\web\Link;
use yii\web\Linkable;
use yii\helpers\Url;
use yii\web\HttpException;
use Yii;
/**
*
* @property integer $ID
* @property string $UF_OPERATOR
* @property integer $UF_CART_NUMBER
* @property double $UF_SUM
* @property integer $UF_KASSA
* @property integer $UF_SHOP
* @property double $UF_SKIDKA
* @property string $UF_XML_ID
* @property string $UF_DATE
* @property string $UF_TOVARS_DOP
*/
class Checks extends ActiveRecord implements Linkable {
const pageSizeMax = 300; // max кол-во элементов в запросе
public static $year_s;
public static $year_f;
public static $_tableName; // название таблиц +
public static $year; // текущий год
/**
* @inheritdoc
*/
public static function tableName() {
return self::$_tableName;
}
/**
* Переопределяем метод, чтобы сбросить КЕШ о инфе об таблицы
* @return type
* @throws InvalidConfigException
*/
public static function getTableSchema() {
$tableSchema = Yii::$app->db->getTableSchema(static::tableName(), true);
if ($tableSchema === null) {
throw new HttpException(422, 'Не найдено чеков за ' . self::$year . ' год', 3);
}
return $tableSchema;
}
/**
* Устанавливает имя таблицы
* @return type
*/
public function SetTableName() {
self::$_tableName = self::CheckYear() . '_checks';
if (Yii::$app->db->getTableSchema(self::$_tableName, true) === null && Yii::$app->request->isPost == true) {
self::CreateTable(self::$_tableName);
}
return self::$_tableName;
}
/**
* Установим год
* @return type
* @throws HttpException
*/
public function SetYear() {
self::$year_s = Yii::$app->controller->module->params['year_s'];
self::$year_f = Yii::$app->controller->module->params['year_f'];
self::$year = Yii::$app->controller->module->params['year'];
}
/**
* Проверка доступности года
* @return type
* @throws HttpException
*/
public function CheckYear() {
if (self::$year < self::$year_s || self::$year > self::$year_f) {
throw new HttpException(422, 'Не верно указан год' , 2);
} else {
return self::$year;
}
}
/**
* Создает таблицу в БД
* @param type $table_name
*/
public static function CreateTable($table_name) {
Yii::$app->db->createCommand()->createTable($table_name, [
'ID' => 'pk',
'UF_OPERATOR' => 'string',
'UF_CART_NUMBER' => 'int(18)',
'UF_SUM' => 'double',
'UF_KASSA' => 'int(18)',
'UF_SHOP' => 'int(18)',
'UF_SKIDKA' => 'double',
'UF_XML_ID' => 'string(50)',
'UF_DATE' => 'datetime',
'UF_TOVARS_DOP' => 'text',
])->execute();
// creates index for column `UF_XML_ID`
Yii::$app->db->createCommand()->createIndex(
'ix_perf_checks_1',
$table_name,
'UF_XML_ID',
true // Уникальный индекс
)->execute();
// creates index for column `UF_XML_ID`
Yii::$app->db->createCommand()->createIndex(
'ix_perf_checks_2',
$table_name,
'UF_CART_NUMBER',
false
)->execute();
}
/**
* Формируем ссылки HATEOAS
* @return type
*/
public function getLinks() {
return [
Link::REL_SELF => Url::to(['checks/view', 'id' => $this->ID, 'year' => self::$year], true),
'GetCheksPozAllLink_Slepok' => $this->GetCheksPoz('GetCheksPozAllLink_Slepok'),
'GetCheksPozAllLink' => $this->GetCheksPoz('GetCheksPozAllLink'),
'UF_TOVARS_DOP' => $this->GetCheksPoz()
];
}
/**
* @inheritdoc
*/
public function rules() {
return [
[['UF_OPERATOR', 'UF_XML_ID', 'UF_TOVARS_DOP'], 'string'],
[['UF_CART_NUMBER', 'UF_KASSA', 'UF_SHOP'], 'integer', 'min' => 1],
[['UF_SUM', 'UF_SKIDKA'], 'number'],
[['UF_DATE'], 'datetime', 'format' => 'php:Y-m-d H:i:s', 'message' => 'Неверный формат значения. Правильный формат: Y-m-d H:i:s'],
[['UF_OPERATOR', 'UF_XML_ID', 'UF_TOVARS_DOP', 'UF_CART_NUMBER', 'UF_KASSA', 'UF_SHOP', 'UF_SUM', 'UF_DATE'], 'required'],
[['UF_OPERATOR', 'UF_XML_ID', 'UF_TOVARS_DOP'], 'trim'],
[[ 'UF_XML_ID'], 'unique'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels() {
return [
'ID' => 'ID',
'UF_OPERATOR' => 'UF_OPERATOR',
'UF_CART_NUMBER' => 'UF_CART_NUMBER',
'UF_SUM' => 'UF_SUM',
'UF_KASSA' => 'UF_KASSA',
'UF_SHOP' => 'UF_SHOP',
'UF_SKIDKA' => 'UF_SKIDKA',
'UF_XML_ID' => 'UF_XML_ID',
'UF_DATE' => 'UF_DATE',
'UF_TOVARS_DOP' => 'UF_TOVARS_DOP',
];
}
/**
* Получаем позиции товара по чеку
* @param string $action действие
* @return array массив ссылок
*/
private function GetCheksPoz($action = '') {
$arr_id_tovs = unserialize($this->UF_TOVARS_DOP);
switch ($action) {
case 'GetCheksPozAllLink':
return Url::to([
'checks-tovs/index',
'year' => self::$year,
'search' => json_encode(['ID' => $arr_id_tovs, 'pageSize' => count($arr_id_tovs)], true),
], true);
case 'GetCheksPozAllLink_Slepok':
return Url::to([
'checks-tovs/index',
'year' => self::$year,
'expand' => 'checksTovsSlepok',
'search' => json_encode(['ID' => $arr_id_tovs, 'pageSize' => count($arr_id_tovs)], true),
], true);
default:
$arr_links = [];
foreach ($arr_id_tovs as $id) {
$arr_links[] = Url::to(['checks-tovs/view', 'id' => $id, 'year' => self::$year], true);
}
return $arr_links;
}
}
}
Комментарии
Добавить комментарий