このパターンに従って、継承されたモデルに共通のテーブルを使用し、このテーブルに
type
フィールドを追加する必要があります。これにより、このレコードの継承クラスが定義されます。
この記事では、次のモデル継承構造を使用します。
Car |- SportCar |- HeavyCar
テーブル
`car`
構造は次のとおりです。
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');
Giiを使用して
Car
モデルを生成できます。
仕組み
単純な
CarQuery
クエリ
CarQuery
が必要に
CarQuery
ます。これは、車のタイプを自動的に置き換えます。
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
から継承クラスを作成できます。 それらの中で、記録する車両のタイプをモデルの
type
フィールドに保存する
TYPE
定数を定義し、ActiveRecordメソッド
init
、
find
および
beforeSave
を再定義します。このタイプは、モデルと
CarQuery
クエリに自動的に置き換えられます。
TYPE
は文字列である必要はなく(unsigned intを使用する方が賢明です)、必ずしも定数である必要はありませんが、簡単にするためにそうします。 これが
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
クラスに入れ、
protected
定数の代わりに
Car::getType
メソッドを使用することで、コードの重複を回避できますが、ここでは簡単にするためにここでは停止しません。
次に、タイプに応じて
Car:instantiate:
メソッドを再定義して、目的のクラスのモデルを自動的に作成する必要があります。
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
すべての後継について知ることは、実際にはあまり良い解決策ではありませんが、これもアプローチの理解を容易にするためだけに行われ、コードを少し複雑にすることで簡単に取り除くことができます。
これで、
single table inheritance
準備がすべて整いました。 コントローラでの透過的な使用の簡単な例を次に示します。
// 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
異なるクラス間でそれらをスキップするように、モデル内で一意としてマークされているフィールドがある場合、このような素晴らしいYii
targetClass
を
targetClass
として使用できます。
public function rules() { return [ [['MyUniqueColumnName'], 'unique', 'targetClass' => Car::classname()], ]; }
これは、Habruch SamDarkが書いたYii2の便利な「レシピ」の1つであるhttps://github.com/samdark/yii2-cookbookの無料翻訳です。それが気に入らなかったので、悪の光線は私に。