2016-09-20 44 views
1

我想使用yii2 ActiveRecord在我的表中插入多條記錄。 我已經知道我可以使用此代碼yii2使用ActiveRecord批量插入

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

但是通過這種方式我的模型驗證不執行。 我已經閱讀了這個問題 ActiveRecord batch insert (yii2)

而且受到一個棘手的方式做驗證,考慮我要填寫使用ActiveRecords事件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 

     } 
    } 

} 

另外,對於更方便使用多種型號可以開發特殊的Collection類,實現\ArrayAccess\Iterator接口。這個集合可以迭代爲簡單的數組,但它包含批量操作的特殊方法。事情是這樣的:

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

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

謝謝您的回答, 如果你的模型有一個BeforeSave事件被插入新記錄之前,所以出事TP所有的記錄,除了批量插入觸發。 我們該怎麼辦這個問題呢? – mhndev

+0

是的,我忘記了事件。我加入了'beforeSave'和'aftreSave'事件的示例支持(基於'\ yii \ db \ ActiveRecord :: insertInternal')。當然,描述的方法並不是'$ model-> save()'的完整選擇。至少我們不能在插入成功後填入PK。 – IStranger