Полиморфно найти модель из базы данных в Yii2

У меня есть одна таблица в базе данных (MySQL). Но эта таблица хранит несколько немного разных типов строк. Тип зависит от этой таблицы type колонка. У меня есть абстрактный класс ActiveRecord для таблицы и несколько дочерних подклассов, реализующих слегка отличную логику для строк одной и той же таблицы разных типов. Сейчас я реализую действие контроллера обновления для всех типов строк. Мне предоставляется идентификатор строки, и мне нужно создать экземпляр ActiveRecord, представляющий строку с этим идентификатором. Но мне как-то нужно создавать экземпляры разных подклассов в зависимости от типа соответствующей строки.

Если бы мне предоставили как тип, так и идентификатор, я мог бы использовать фабрику, чтобы выбрать соответствующий подкласс. Но я уже могу иметь тип в базе данных, и идентификатор дает мне достаточно информации, чтобы выбрать его оттуда. Но если бы я сначала выбрал тип из базы данных, а затем создал экземпляр соответствующего подкласса, это означало бы выполнение одного и того же запроса дважды.

Я хочу найти хороший способ получить данные из базы данных, а затем выбрать правильный подкласс ActiveRecord, чтобы создать для него экземпляр, не делая чрезмерных запросов и не требуя избыточных данных. Есть ли способ сделать это Yii2?

Или я должен подойти к этой проблеме как-то иначе? Проблема в том, что в одной таблице хранятся несколько почти одинаковых, но немного разных сущностей с немного другой бизнес-логикой.

3

Решение

Один из подходов к этой проблеме называется «наследование одной таблицы» и описан Мартином Фаулером. Вот. Также есть хорошая статья о его реализации в Yii 2 в samdarkкулинарная книга (одна из основных авторов Yii 2), которая в настоящее время находится в процессе написания, но доступна на Github.

Я не собираюсь копировать всю статью, но одной ссылки недостаточно. Вот несколько важных вещей:

1) Создайте общий запрос для всех типов объектов (например, автомобилей):

namespace app\models;

use yii\db\ActiveQuery;

class CarQuery extends ActiveQuery {
public $type;

public function prepare($builder)
{
if ($this->type !== null) {
$this->andWhere(['type' => $this->type]);
}
return parent::prepare($builder);
}
}

2) Создайте отдельную модель для каждого типа (исходя из общей модели автомобиля):

Спортивные машины:

namespace app\models;

class SportCar extends Car
{
const TYPE = 'sport';

public static function find()
{
return new CarQuery(get_called_class(), ['type' => self::TYPE]);
}

public function beforeSave($insert)
{
$this->type = self::TYPE;
return parent::beforeSave($insert);
}
}

Тяжелые машины:

namespace app\models;

class HeavyCar extends Car
{
const TYPE = 'heavy';

public static function find()
{
return new CarQuery(get_called_class(), ['type' => self::TYPE]);
}

public function beforeSave($insert)
{
$this->type = self::TYPE;
return parent::beforeSave($insert);
}
}

3) Override instantiate() метод в Car Модель для возврата правильного типа автомобилей:

public static function instantiate($row)
{
switch ($row['type']) {
case SportCar::TYPE:
return new SportCar();
case HeavyCar::TYPE:
return new HeavyCar();
default:
return new self;
}
}

Тогда вы можете использовать любой тип автомобилей индивидуально, как обычные модели.

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]