2015-03-20 2 views
3

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

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

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

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

ответ

5

Один из подходов к этой проблеме называется «Наследование одиночной таблицы» и описано Мартином Фаулером here. Существует также хорошая статья о ее внедрении в 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; 
    } 
} 

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

+1

Таким образом, переопределение 'instantiate' позволяет мне выбирать из разных подклассов ActiveRecord в соответствии с данными, полученными из базы данных. Именно об этом я и спрашивал! Таким образом, мне не нужно будет предоставлять тип моему контроллеру при работе с уже существующей строкой. Большое спасибо! – Gherman

Смежные вопросы