You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中saveMany()/createMany()执行多插入查询而非单查询问题

问题描述

使用Laravel 9 + PHP 8.1.0开发,Order与OrderProduct是一对多(HasMany/BelongsTo)关联,保存订单项时,期望通过saveMany()用单条批量插入查询完成操作,但实际生成了N条独立的INSERT语句,切换到createMany()后结果一致。

代码逻辑是从未入库的$calculation模型实例中获取产品数据,通过foreach创建本地OrderProduct对象,再调用saveMany()保存。

相关代码

问题函数

public function createOrderItems(Order $calculation, Order $order): void
{
    $order_products = [];

    foreach ($calculation->getProducts() as $product) {
        $order_products[] = new OrderProduct([
            'product_id' => $product->product_id,
            'quantity' => $product->quantity,
            'price_with_pdv' => $product->price_with_pdv,
            'price_without_pdv' => $product->price_without_pdv,
            'weight' => $product->weight,
        ]);
    }

    $order->orderProducts()->saveMany($order_products);
}

函数调用

$this->createOrderItems($calculation, $order);

Order模型关联

public function orderProducts(): HasMany
{
    return $this->hasMany(OrderProduct::class);
}

Telescope日志

insert into `order_products` (`product_id`, `quantity`, `price_with_pdv`, `price_without_pdv`, `weight`...  0.33ms  
insert into `order_products` (`product_id`, `quantity`, `price_with_pdv`, `price_without_pdv`, `weight`...  0.34ms  
insert into `order_products` (`product_id`, `quantity`, `price_with_pdv`, `price_without_pdv`, `weight`...  0.50ms  
insert into `order_products` (`product_id`, `quantity`, `price_with_pdv`, `price_without_pdv`, `weight`...  0.30ms  
insert into `order_products` (`product_id`, `quantity`, `price_with_pdv`, `price_without_pdv`, `weight`...  0.45ms
解决方案

原因说明

Laravel的saveMany()和createMany()方法并非执行批量插入,它们会循环处理每个模型实例,单独执行INSERT语句。这是因为这两个方法需要触发模型的creating/created事件、处理观察者、自动填充时间戳(如created_at/updated_at)以及关联的外键赋值,无法通过单条批量插入完成这些逻辑。

实现批量插入的方法

如果不需要触发模型事件,仅需高效批量插入数据,可以直接构建批量数据数组,使用OrderProduct::insert()实现:

public function createOrderItems(Order $calculation, Order $order): void
{
    $orderProductsData = [];
    $now = now(); // 手动填充时间戳(如果模型开启$timestamps=true)

    foreach ($calculation->getProducts() as $product) {
        $orderProductsData[] = [
            'order_id' => $order->id, // 手动赋值外键
            'product_id' => $product->product_id,
            'quantity' => $product->quantity,
            'price_with_pdv' => $product->price_with_pdv,
            'price_without_pdv' => $product->price_without_pdv,
            'weight' => $product->weight,
            'created_at' => $now,
            'updated_at' => $now,
        ];
    }

    OrderProduct::insert($orderProductsData);
}

注意事项

  • 如果需要触发模型事件或使用Eloquent的其他特性,只能接受saveMany()的单条插入逻辑,或者手动触发对应事件。
  • 批量插入时,需要手动处理外键(order_id)和时间戳字段(如果模型开启了$timestamps)。
  • 确保批量数据数组的每个元素字段与数据库表字段完全匹配,避免字段缺失或错误。

内容的提问来源于stack exchange,提问作者Leon Kardaš

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:35:15