type
field that will define the class that inherits this record. Car |- SportCar |- HeavyCar
`car`
has the following structure: CREATE TABLE `car` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `type` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ); INSERT INTO `car` (`id`, `name`, `type`) VALUES (1, 'Kamaz', 'heavy'), (2, 'Ferrari', 'sport'), (3, 'BMW', 'city');
Car
model can be generated with Gii.CarQuery
, which will automatically substitute the type of car. 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); } }
Car
. In them, we define the TYPE
constant that will store the type of car for writing in the type
field of the model, and override the ActiveRecord methods of init
, find
and beforeSave
, in which this type will be automatically inserted into the model and into the CarQuery
query. TYPE
does not have to be a string (it is wiser to use an unsigned int), and not even necessarily a constant, but for simplicity, let's do this. This will be SportCar
: namespace app\models; class SportCar extends Car { const TYPE = 'sport'; public function init() { $this->type = self::TYPE; parent::init(); } 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); } }
HeavyCar
: namespace app\models; class HeavyCar extends Car { const TYPE = 'heavy'; public function init() { $this->type = self::TYPE; parent::init(); } 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); } }
Car
class and using the Car::getType
method instead of the protected
constant, but now I will not dwell on this for simplicity.Car:instantiate:
method to automatically create the model of the desired class, depending on the type: public static function instantiate($row) { switch ($row['type']) { case SportCar::TYPE: return new SportCar(); case HeavyCar::TYPE: return new HeavyCar(); default: return new self; } }
switch case
in the code of the parent model is actually not a very good solution, but, again, this is done only for ease of understanding the approach and it is easy to get rid of it by slightly complicating the code.single table inheritance
everything is ready. Here is a simple example of its transparent use in the controller: // finding all cars we have $cars = Car::find()->all(); foreach ($cars as $car) { echo "$car->id $car->name " . get_class($car) . "<br />"; } // finding any sport car $sportCar = SportCar::find()->limit(1)->one(); echo "$sportCar->id $sportCar->name " . get_class($sportCar) . "<br />";
1 Kamaz app\models\HeavyCar 2 Ferrari app\models\SportCar 3 BMW app\models\Car 2 Ferrari app\models\SportCar
UniqueValidator
skip them from different classes, you can use such a pleasant Yii chip as the targetClass
: public function rules() { return [ [['MyUniqueColumnName'], 'unique', 'targetClass' => Car::classname()], ]; }
Source: https://habr.com/ru/post/274925/
All Articles