このパターンに従って、継承されたモデルに共通のテーブルを使用し、このテーブルに
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の無料翻訳です。それが気に入らなかったので、悪の光線は私に。