2016-09-20 23 views
1

yii2 ActiveRecordを使用してテーブルに複数のレコードを挿入したいとします。 私はすでに私はこのコードActiveRecordを使用したyii2バッチインサート

$connection->createCommand()->batchInsert('user', ['name', 'age'], [ 
    ['Tom', 30], 
    ['Jane', 20], 
    ['Linda', 25], 
])->execute(); 

を使用することができることを知っているが、このアプローチによって、私のモデルの検証が実行されていません。 と私はすでにこの質問ActiveRecord batch insert (yii2)

だけでなく、トリッキーな方法で検証を行うことによって を読みましたが、私はActiveRecordを使用してイベントをcreated_atupdated_at列を埋めるためにしたいことを検討してください。

ちょうどこの

public function beforeSave($insert) 
{ 
    if (parent::beforeSave($insert)) { 
     if($insert) 
      $this->created_at = date('Y-m-d H:i:s'); 
     $this->updated_at = date('Y-m-d H:i:s'); 
     return true; 
    } else { 
     return false; 
    } 
} 

答えて

2

ように私はそれがモデルをトリガーするためbeforeSaveイベント(および同様のもの)を使用することをお勧めではないと思います。しかし、一度にを複数のモデルを保存したい。私は一括メソッドを使用することをお勧めします。私は通常「バルク」アプローチ以下の使用類似事例において

(コードは、単なる例について試験せず):

namespace common\components; 

class Model extends yii\base\Model { 

    /** 
    * Saves multiple models. 
    * 
    * @param ActiveRecord[] $models 
    * @return bool 
    */ 
    public static saveMultiple($models){ 

     if(count($models) > 0){ 

      $firstModel  = reset($models); 
      $columnsToInsert = $firstModel->attributes(); // here you can remove excess columns. for example PK column. 
      $modelsToInsert = []; 
      $rowsToInsert = []; 

      foreach($models as $model){ 
       if ($this->beforeSave(true)) { 
        $modelsToInsert[] = $model; 
       } 
      } 

      foreach($modelsToInsert as $model){ 
       $rowsToInsert[] = array_values($model->attributes);  // here you can remove excess values 
      } 

      $numberAffectedRows = \Yii::$app->db->createCommand() 
       ->batchInsert($firstModel->tableName(), $columnsToInsert, $rowsToInsert) 
       ->execute(); 

      $isSuccess = ($numberAffectedRows === count($models)); 

      if($isSuccess){ 
       $changedAttributes = array_fill_keys($columnsToInsert, null); 

       foreach($modelsToInsert as $model){ 
        $model->afterSave(true, $changedAttributes); 
       } 
      } 

      return $isSuccess; 

     } else { 

      return true; 
     } 

    } 

} 

このクラスを使用することができる。

use common\components\Model; 

/** 
* @var SomeActiveRecord[] $models Array that contains array of active records (type SomeActiveRecord) 
*/ 

// ... 

if (Model::validateMultiple($models)){ 

    if(!Model::saveMultiple($models)){ 
     // ... some error handling 
    } 

} else { 

    foreach($models as $model){ 
     if($model->hasErrors()){ 

      $errors = $model->getFirtsErrors(); 
      // ... some error handling 

     } 
    } 

} 

また、より便利にするために複数のモデルを扱うことにより、\ArrayAccess\Iteratorのインタフェースを実装する特別なCollectionクラスを開発することができます。このコレクションは単純な配列として反復することができますが、バルク操作のための特別なメソッドが含まれています。このような何か:あなたの答えのための

foreach($modelCollection as $model){ 
    // ... 
} 

$modelCollection->validate(); // works similar to common\components\Model::validateMultiple() 
$modelCollection->save();  // works similar to common\components\Model::saveMultiple() 
+0

おかげで、 モデルがバッチ挿入以外のすべてのあなたの記録tpは起こって何かがあるように、新しいレコードを挿入する前にトリガされbeforeSaveイベントを持っている場合。 この問題はどうしたらいいですか? – mhndev

+0

はい、私はイベントについて忘れました。私は 'beforeSave'と' aftreSave'イベント( '\ yii \ db \ ActiveRecord :: insertInternal'に基づく)のサンプルサポートを追加しました。もちろん、記述されたアプローチは '$ model-> save()'の完全な代替ではありません。挿入が成功した後、少なくともPKを埋めることはできません。 – IStranger

関連する問題