Laravel中使用Eloquent ORM进行批量插入

233

如何使用Eloquent ORM在Laravel中执行批量数据库插入?

我正在使用XML文档并循环遍历其元素。我想在Laravel中实现类似以下的操作:

$sXML = download_page('http://remotepage.php&function=getItems&count=100&page=1');
$oXML = new SimpleXMLElement($sXML);
$query = "INSERT INTO tbl_item (first_name, last_name, date_added) VALUES";
foreach($oXML->results->item->item as $oEntry){
    $query .=  "('" . $oEntry->firstname . "', '" . $oEntry->lastname . "', '" . date("Y-m-d H:i:s") . "'),";
}
mysql_query($query);

但是我得到了以下错误。

SQLSTATE[HY093]: 无效参数号:命名参数和位置参数混合。


1
你的模型上有has_many关联吗? - PapaSmurf
1
@jonathandey 我目前没有任何感情关系。 - phoenixwizard
@DavidBarker 我已经尝试使用for循环来形成查询字符串。我还尝试在Laravel中使用事务。 - phoenixwizard
你看过http://laravel.com/docs/database/eloquent#mass-assignment吗? - BenjaminRH
1
@BenjaminRH 大规模赋值是自动将输入分配给单个模型字段,而不是一次创建多个模型的过程。 - Charles Wood
显示剩余2条评论
12个回答

437
你可以直接使用 Eloquent::insert() 方法。

例如:

$data = [
    ['name'=>'Coder 1', 'rep'=>'4096'],
    ['name'=>'Coder 2', 'rep'=>'2048'],
    //...
];

Coder::insert($data);

4
是的,它仍适用于Laravel 4。 - Ola
70
值得注意的是,实际上它并没有涉及到“Eloquent”。它只是将调用代理到“Query\Builder@insert()”方法。使用“Eloquent”不能高效地插入多行数据,也没有提供任何批量插入的方法。 - Jarek Tkaczyk
11
我们需要更新/创建时间戳,应该怎么做?@CanVural - Milan Maharjan
2
我们如何获取所有新插入行的ID? - akshaykumar6
4
这将使用一个插入。因此,对于足够大的数组,它将失败。 - patrox
显示剩余14条评论

102

我们可以轻松地更新GTF答案以更新时间戳

$data = array(
    array(
        'name'=>'Coder 1', 'rep'=>'4096',
        'created_at'=>date('Y-m-d H:i:s'),
        'modified_at'=> date('Y-m-d H:i:s')
       ),
    array(
         'name'=>'Coder 2', 'rep'=>'2048',
         'created_at'=>date('Y-m-d H:i:s'),
         'modified_at'=> date('Y-m-d H:i:s')
       ),
    //...
);

Coder::insert($data);

更新: 为了简化日期,我们可以像@Pedro Moreira建议的那样使用carbon。

$now = Carbon::now('utc')->toDateTimeString();
$data = array(
    array(
        'name'=>'Coder 1', 'rep'=>'4096',
        'created_at'=> $now,
        'modified_at'=> $now
       ),
    array(
         'name'=>'Coder 2', 'rep'=>'2048',
         'created_at'=> $now,
         'modified_at'=> $now
       ),
    //...
);

Coder::insert($data);

更新2:对于Laravel 5,请使用updated_at代替modified_at

$now = Carbon::now('utc')->toDateTimeString();
$data = array(
    array(
        'name'=>'Coder 1', 'rep'=>'4096',
        'created_at'=> $now,
        'updated_at'=> $now
       ),
    array(
         'name'=>'Coder 2', 'rep'=>'2048',
         'created_at'=> $now,
         'updated_at'=> $now
       ),
    //...
);

Coder::insert($data);

45
可以在脚本开头使用 Carbon 来定义 $now 变量:$now = Carbon::now('utc')->toDateTimeString();。然后对于每次插入只需使用 'created_at' => $now, 'updated_at' => $now - Pedro Moreira
2
我们如何获取所有新插入行的ID? - akshaykumar6
2
为什么要使用 'utc'?这是项目的偏好,还是 Eloquent 总是在 'utc' 中工作? - pilat
9
我不想引发一场关于“空格 vs 制表符”的争论,但请保存UTC时间戳!这将在以后为您节省大量的痛苦!考虑全球用户 :) - iSS
6
如果我可以问一下,在这种情况下,使用Carbon有什么重要性?使用date("Y-m-d H:i:s")有什么问题吗? - Ifedi Okonkwo
显示剩余4条评论

45

这是更加优美的实现方式,

    $allinterests = [];
    foreach($interests as $item){ // $interests array contains input data
        $interestcat = new User_Category();
        $interestcat->memberid = $item->memberid;
        $interestcat->catid = $item->catid;
        $allinterests[] = $interestcat->attributesToArray();
    }
    User_Category::insert($allinterests);

31

阅读此文的各位,请查看createMany()方法

/**
 * Create a Collection of new instances of the related model.
 *
 * @param  array  $records
 * @return \Illuminate\Database\Eloquent\Collection
 */
public function createMany(array $records)
{
    $instances = $this->related->newCollection();

    foreach ($records as $record) {
        $instances->push($this->create($record));
    }

    return $instances;
}

40
这并不是所谓的批量插入。由于实现不佳,该函数将为每个项目准备和执行相同的查询。 - Paul Spiegel
11
值得注意的是,这是一种关系方法,不能直接从模型中调用,例如 Model::createMany() - digout

9

我经过多次搜索,最终使用以下自定义时间戳来完成:

$now = Carbon::now()->toDateTimeString();
Model::insert([
    ['name'=>'Foo', 'created_at'=>$now, 'updated_at'=>$now],
    ['name'=>'Bar', 'created_at'=>$now, 'updated_at'=>$now],
    ['name'=>'Baz', 'created_at'=>$now, 'updated_at'=>$now],
    ..................................
]);

6

Eloquent::insert 是正确的解决方案,但它不会更新时间戳,所以你可以像下面这样做:

 $json_array=array_map(function ($a) { 
                        return array_merge($a,['created_at'=> 
                                            Carbon::now(),'updated_at'=> Carbon::now()]
                                           ); 
                                     }, $json_array); 
 Model::insert($json_array);

在进行插入操作之前,想法是在整个数组上添加created_at和updated_at。


1

从 Laravel 5.7 开始,你可以使用 Illuminate\Database\Query\Builder 的 insertUsing 方法。

$query = [];
foreach($oXML->results->item->item as $oEntry){
    $date = date("Y-m-d H:i:s")
    $query[] = "('{$oEntry->firstname}', '{$oEntry->lastname}', '{$date}')";
}

Builder::insertUsing(['first_name', 'last_name', 'date_added'], implode(', ', $query));

0
也许更符合 Laravel 的解决方案是使用集合并循环插入模型,利用时间戳。
<?php

use App\Continent;
use Illuminate\Database\Seeder;

class InitialSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        collect([
            ['name' => 'América'],
            ['name' => 'África'],
            ['name' => 'Europa'],
            ['name' => 'Asia'],
            ['name' => 'Oceanía'],
        ])->each(function ($item, $key) {
            Continent::forceCreate($item);
        });
    }
}

编辑:

抱歉我误解了。对于批量插入,这可能会有所帮助,也许您可以制作良好的种子并对其进行优化。

<?php

use App\Continent;
use Carbon\Carbon;
use Illuminate\Database\Seeder;

class InitialSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $timestamp = Carbon::now();
        $password = bcrypt('secret');

        $continents = [
            [
                'name' => 'América'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'África'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'Europa'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'Asia'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'Oceanía'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
        ];

        Continent::insert($continents);
    }
}

2
这将为每个项目执行一个查询。这不是批量插入。 - Emile Bergeron
1
@EmileBergeron 我同意你的观点。我已经编辑了我的帖子,这可能有助于实现良好的批量插入。考虑将需要大量时间的任务从循环中剔除(如carbon、bcrypt),这可以节省大量时间。 - Francisco Daniel
@FranciscoDaniel 非常感谢您的建议,虽然它在仅插入操作时运行良好,但如果有人能够建议我是否有一种方法可以使用批量数组更新或插入数据,那将会很有帮助。 - Khyati Bhojawala

0
$start_date = date('Y-m-d h:m:s');        
        $end_date = date('Y-m-d h:m:s', strtotime($start_date . "+".$userSubscription['duration']." months") );
        $user_subscription_array = array(
          array(
            'user_id' => $request->input('user_id'),
            'user_subscription_plan_id' => $request->input('subscription_plan_id'),
            'name' => $userSubscription['name'],
            'description' => $userSubscription['description'],
            'duration' => $userSubscription['duration'],
            'start_datetime' => $start_date,
            'end_datetime' => $end_date,
            'amount' => $userSubscription['amount'],
            'invoice_id' => '',
            'transection_datetime' => '',
            'created_by' => '1',
            'status_id' => '1', ),
array(
            'user_id' => $request->input('user_id'),
            'user_subscription_plan_id' => $request->input('subscription_plan_id'),
            'name' => $userSubscription['name'],
            'description' => $userSubscription['description'],
            'duration' => $userSubscription['duration'],
            'start_datetime' => $start_date,
            'end_datetime' => $end_date,
            'amount' => $userSubscription['amount'],
            'invoice_id' => '',
            'transection_datetime' => '',
            'created_by' => '1',
            'status_id' => '1', )
        );
        dd(UserSubscription::insert($user_subscription_array));

UserSubscription 是我的模型名称。 如果成功插入,它将返回“true”,否则返回“false”。


-3

您可以逐个创建并获取模型实例的返回集合

  $createdCollection= collect([
            ['name' => 'América'],
            ['name' => 'África'],
            ['name' => 'Europa'],
            ['name' => 'Asia'],
            ['name' => 'Oceanía'],
        ])->map(function ($item, $key) {
            return Continent::create($item);
        });

我认为这不是批量插入。 - lechat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接