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メソッド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の無料翻訳です。それが気に入らなかったので、悪の光線は私に。



All Articles