yii2 使用 ActiveRecord 批量插入
Posted
技术标签:
【中文标题】yii2 使用 ActiveRecord 批量插入【英文标题】:yii2 batch insert with ActiveRecord 【发布时间】:2017-01-28 17:39:33 【问题描述】:我想使用 yii2 ActiveRecord 在我的表中插入多条记录。 我已经知道我可以使用这段代码了
$connection->createCommand()->batchInsert('user', ['name', 'age'], [
['Tom', 30],
['Jane', 20],
['Linda', 25],
])->execute();
但是通过这种方法,我的模型验证没有执行。 我已经读过这个问题 ActiveRecord batch insert (yii2)
但也通过以一种棘手的方式进行验证,考虑我想使用 ActiveRecords 事件填充 created_at
和 updated_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;
【问题讨论】:
【参考方案1】:我认为使用 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()
【讨论】:
感谢您的回答,如果您的模型有一个 beforeSave 事件,该事件在插入新记录之前触发,那么除了批量插入之外,您的所有记录都会发生一些事情。这个问题我们该怎么办? 是的,我忘记了事件。我添加了对beforeSave
和aftreSave
事件的示例支持(基于\yii\db\ActiveRecord::insertInternal
)。当然,所描述的方法并不是$model->save()
的完全替代方案。至少插入成功后我们不能填充PK。以上是关于yii2 使用 ActiveRecord 批量插入的主要内容,如果未能解决你的问题,请参考以下文章