Yii2の単一テーブル(単一テーブル継承パターン)を記述するActiveRecordの継承

残念ながら、ほとんどのリレーショナルデータベースには継承のサポートがないため、これを手動で実装する必要があります。 この記事では、Martin Fowler著の「Patterns of Enterprise Application Architecture」という本で説明されている「単一テーブル継承」などの継承へのアプローチを実装する方法を簡単に示したいと思います。

このパターンに従って、継承されたモデルに共通のテーブルを使用し、このテーブルに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メソッドinitfindおよび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 targetClasstargetClassとして使用できます。

  public function rules() { return [ [['MyUniqueColumnName'], 'unique', 'targetClass' => Car::classname()], ]; } 


これは、Habruch SamDarkが書いたYii2の便利な「レシピ」の1つであるhttps://github.com/samdark/yii2-cookbookの無料翻訳です。それが気に入らなかったので、悪の光線は私に。

Source: https://habr.com/ru/post/J274925/


All Articles