Yii2 динамические имена таблиц для модели, несколько таблиц БД для одной модели

Дата публикации: 19 September 2017, Категория: Yii2

погуглив проблему динамических таблиц для модели в yii2, нашел интересный пост на эту тему https://habrahabr.ru/sandbox/89573/.

в кратце:

  1. Создаем статическое свойство модели
    public static $_tableName;   // название таблиц +​
  2. Переписываем метод получения имени таблицы для работы модели с БД
    public static function tableName() {
    		return self::$_tableName;
    	}​
  3. Пишим метод, который установит имя таблицы
    /**
    	 * Устанавливает имя таблицы
    	 * @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;
		}
	}

}

Комментарии

No results found.

Добавить комментарий