yii2批处理插入与ActiveRecord [英] yii2 batch insert with ActiveRecord

查看:281
本文介绍了yii2批处理插入与ActiveRecord的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用yii2 ActiveRecord在我的表中插入多个记录。
我已经知道我可以使用这个代码

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

但是通过这种方法我的模型验证没有执行。
,我已阅读过此问题
ActiveRecord批处理插入(yii2)



但是通过以棘手的方式进行验证,考虑我想填写 created_at 和<$ c $

 

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


解决方案

我认为使用 beforeSave 事件(和类似的东西)是不好的,因为它会触发每个模型。但是,您希望一次保存多个模型。我建议使用批量方法。



在类似的情况下,我通常使用批量方法(代码未测试,只是为例):

 命名空间common\components; 

类模型扩展yii \base\Model {

/ **
*保存多个模型。
*
* @param ActiveRecord [] $ models
* @return bool
* /
public static saveMultiple($ models){

if(count($ models)> 0){

$ firstModel = reset($ models);
$ columnsToInsert = $ firstModel-> attributes(); //这里你可以删除多余的列。例如PK柱。
$ modelsToInsert = [];
$ rowsToInsert = [];

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

foreach($ modelsToInsert as $ model){
$ rowsToInsert [] = array_values($ model-> attributes); //这里你可以删除多余的值
}

$ 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包含活动记录数组(类型SomeActiveRecord)的数组
* /

...

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

if(!Model :: saveMultiple($ models)){
// ...一些错误处理
}

} else {

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

$ errors = $ model-> getFirtsErrors();
// ...一些错误处理

}
}

}

此外,为了更方便地使用多个模型,可以开发特殊的 Collection \ArrayAccess 和 \Iterator 接口。此集合可以迭代为简单数组,但它包含批量操作的特殊方法。像这样:

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

$ modelCollection-> validate(); //工作类似于common\components\Model :: validateMultiple()
$ modelCollection-> save(); // works like common\components\Model :: saveMultiple()


I want to insert multiple record in my table using yii2 ActiveRecord. I already know that I can use this code

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

but by this approach my model validations are not executing. and I already have read this question ActiveRecord batch insert (yii2)

but also by doing validation in a tricky way, consider I want to fill created_at and updated_at columns using ActiveRecords events.

just like this

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;
    }
}

解决方案

I think is not good idea to use beforeSave events (and similar stuff) because it will trigger for each model. However you want save multiple models at once. I recommend to use bulk methods.

In similar cases I use usually following "bulk" approach (code not tested, just for example):

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;
        }

    }

}

This class can be used:

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

        }
    }

}

Additionally, for more convenient working with multiple models can be developed special Collection class that implements \ArrayAccess and \Iterator interfaces. This collection can iterated as simple array, however it contains special methods for bulk operations. Something like this:

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

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

这篇关于yii2批处理插入与ActiveRecord的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆