Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Car не всегда хорошая идея. Я вижу две стратегии:Car — суперкласс. Есть базовые правила валидации того, что есть у всех, типа [['type', 'name'], 'required'], но создание машин через этот класс невозможно, обязательно использовать уточненные классы. Это хорошо, когда каждый тип гарантированно имеет что-то свое, чего принципиально не может быть у базового и не пересекается с другими. Например, у спортивной машины, наверняка, не будет сцепного устройства для прицепа :)Car — общий класс машины. Применим, когда удовлетворяет требованиям для создания большинства записей, но лишь некоторые типы содержат что-то специфической и требуют отдельных правил, которые будут вынесены в отдельный класс.Дублирования кода, можно избежать, вынеся эти методы в класс Car и используя вместо константы protected метод Car::getType, но сейчас я не буду на этом останавливаться для простоты.
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, как это часто бывает, лучше всё-таки подумать над формализацией данных на уровне БД, а не на уровне кода.HeavyCar::init(), который выглядит так, будто я пытаюсь вызывать статический метод init, хотя он при этом не статический. В PHP на письме принято методы класса показывать именно таким образом — вне зависимости от того, статичный метод или нет. Просто так принято писать. В официальной документации, например, так и делается. В самом коде, конечно же, я бы использовал коррекный способ вызова в зависимости от статичности метода.
Наследование ActiveRecord's, описывающих одну таблицу (паттерн single table inheritance) в Yii2