Comments 15
О черезмерно толстых моделях писал nepster09 в своей недавней статье Разработка приложений на Yii2 без опыта — прямой путь в АД, но по моему мнению, проблема сводится к сугубо архитектурной и решается очень аккуратно, хороший пример Tairesh и показал.
Такая структура моделей может быть особенно полезной, когда разные типы одного объекта требуют разных правил валидации или специфического поведения. Описывание особенностей разных типов одной модели в одном классе быстро превращает его в сложноподдерживаемое месиво. А если еще и добавить сценарии — можно плакать. Проверено :)
Что касается практики, я бы добавил, что создание объектов базового класса
1)
2)
Открытым может оставаться вопрос о правильном наследовании правил валидации и возможностям комбинирования похожих свойств, но
это может сильно отличатся в разных предметных областях.
Такая структура моделей может быть особенно полезной, когда разные типы одного объекта требуют разных правил валидации или специфического поведения. Описывание особенностей разных типов одной модели в одном классе быстро превращает его в сложноподдерживаемое месиво. А если еще и добавить сценарии — можно плакать. Проверено :)
Что касается практики, я бы добавил, что создание объектов базового класса
Car
не всегда хорошая идея. Я вижу две стратегии:1)
Car
— суперкласс. Есть базовые правила валидации того, что есть у всех, типа [['type', 'name'], 'required']
, но создание машин через этот класс невозможно, обязательно использовать уточненные классы. Это хорошо, когда каждый тип гарантированно имеет что-то свое, чего принципиально не может быть у базового и не пересекается с другими. Например, у спортивной машины, наверняка, не будет сцепного устройства для прицепа :)2)
Car
— общий класс машины. Применим, когда удовлетворяет требованиям для создания большинства записей, но лишь некоторые типы содержат что-то специфической и требуют отдельных правил, которые будут вынесены в отдельный класс.Открытым может оставаться вопрос о правильном наследовании правил валидации и возможностям комбинирования похожих свойств, но
это может сильно отличатся в разных предметных областях.
+3
Стоило упомянуть, что это перевод написанного мной одного из рецептов для Yii 2.0 Cookbook. За перевод спасибо.
+8
Блин, а я думал, что у меня дежавю..))
0
Из песочницы нельзя отметить публикацию как перевод, ссылку добавил. Огромное вам спасибо за замечательные рецепты! Не думал, что автор русскоязычный, да ещё и есть на хабре.
0
да еще и core-developer yii
+2
Если ещё переводить будете, имейте ввиду, что степень готовности там разная. Если у этот рецепт готов на 99%, то некоторые ещё совсем черновики.
0
просто как мнение — может быть интереснее и правильнее было бы сделать этот пример, показывая, что модели эти могут иметь разные поля и как этим всем управлять
0
Из названия статьи показалось, что в yii2, точнее в его AR есть реализация STI. Но нет.
А за статью спасибо, я реализовывал наследование в моделях более хардко(р)но :)
А за статью спасибо, я реализовывал наследование в моделях более хардко(р)но :)
0
Дублирования кода, можно избежать, вынеся эти методы в класс Car и используя вместо константы protected метод Car::getType, но сейчас я не буду на этом останавливаться для простоты.
Можно и не использовать. Достаточно будет описать эти методы в классе Car таким образом:
class Car extends ActiveRecord
{
const TYPE = 'car';
public function init()
{
$this->type = static::TYPE;
parent::init();
}
public static function find()
{
return new CarQuery(get_called_class(), ['type' => static::TYPE]);
}
public function beforeSave($insert)
{
$this->type = static::TYPE;
return parent::beforeSave($insert);
}
}
Ключевое слово
static
как раз будет указывать, что речь идёт о том классе, который наследует логику, а не о том классе, в котором эта логика прописана (как происходит при использовании self
). Соответственно, если класс HeavyCar
расширяет класс Car
, то при вызове метода HeavyCar::init()
инструкция $this->type = static::TYPE;
будет, по сути, исполняться как $this->type = HeavyCar::TYPE;
, а при вызове SportCar::init()
будет исполняться как $this->type = SportCar::TYPE;
. При этом, если в дочернем классе нет константы TYPE
, то она будет искаться в родительском классе. Вот пример, как работает static
:class ParentClass
{
const TYPE = 'parent';
public function getType()
{
return static::TYPE;
}
}
class ChildOne extends ParentClass
{
const TYPE = 'child one';
}
class ChildTwo extends ParentClass
{}
$instance = new ParentClass();
echo $instance->getType() . PHP_EOL; // parent
$instance = new ChildOne();
echo $instance->getType() . PHP_EOL; // child one
$instance = new ChildTwo();
echo $instance->getType() . PHP_EOL; // parent
А с самим подходом, когда для работы с одной и той же таблицей используются несколько моделей, я не согласен, потому что появится соблазн ввести в таблице
Car
поле, хранящее JSON-кодированную (или ещё каким-нибудь способом сериализованную) структуру данных с информацией о прицепах для HeavyCar
. В случае с СУБД, которые поддерживают JSON это может быть нормально, но если используется MySQL, как это часто бывает, лучше всё-таки подумать над формализацией данных на уровне БД, а не на уровне кода.+2
Я уточню на всякий случай про
HeavyCar::init()
, который выглядит так, будто я пытаюсь вызывать статический метод init, хотя он при этом не статический. В PHP на письме принято методы класса показывать именно таким образом — вне зависимости от того, статичный метод или нет. Просто так принято писать. В официальной документации, например, так и делается. В самом коде, конечно же, я бы использовал коррекный способ вызова в зависимости от статичности метода.+2
Буквально вчера читал про универсальные модули комментариев для YII2. Универсальные — в том смысле что один и тот же объект комментария может быть привязан к любому другому объекту (пост в блог, товар в каталоге и т.п.) одинаковым образом. Там одним из ключевых вопросов была уникальность идентификаторов объектов (id поста и id товара могут совпадать). Вот тут что-то похожее буду пробовать освоить в контексте этой задачи. Спасибо.
0
А если еще, разделить Модель на Объект поведения/состояния и репозиторий, то и вовсе можно будет паттерн называть Domain model pattern / Repository pattern :D
+1
А зачем вручную фактически устанавливать связь типов и классов, если можно в поле БД в базовой модели записывать имя класса, а в instantiate создавать модель по полю в БД?
0
Sign up to leave a comment.
Наследование ActiveRecord's, описывающих одну таблицу (паттерн single table inheritance) в Yii2