推荐使用结构化数组命名(如products1)配合request()->get('products')直接解析,避免values_1[]等硬编码方式,提升健壮性与可维护性。

直接用 request()->get() 拿结构化数组,别拼 values_1[] 这种字段名
前端提交带 ID 分组的表单(比如多行商品评论、多组权限配置),最省事且健壮的方式是用 PHP 数组语法命名:name="products[1][comment]"、name="products[2][status]"。Laravel 自动解析成关联数组,request()->get('products') 返回的就是键为 ID、值为子数组的结构,不需要正则提取或硬编码循环 ID。
常见错误是前端写成 values_1[]、values_2[],后端还得用 array_filter(array_keys($request->all()), fn($k) => str_starts_with($k, 'values_')) 去捞键名——既脆弱又难测。只要命名带方括号嵌套,request()->get() 就能原样还原层级。
- ✅ 推荐 HTML:
<input name="products[{{ $id }}][name]"> - ❌ 避免 HTML:
<input name="product_name_{{ $id }}"> - 提交后
$request->get('products')是类似[1 => ['name' => 'A'], 2 => ['name' => 'B']]的结构
insert() 要求字段名完全一致,时间戳必须手动补
insert() 不走模型生命周期,也不管 $fillable,它只认字段名和值的严格对应。如果你从 request()->get('products') 拿到的数据缺 created_at 或字段名拼错(比如前端传 user_name,数据库字段是 name),就会报 SQLSTATE[HY093] 或静默丢数据。
- 所有子数组的 key 必须完全相同,不能有的有
updated_at、有的没有 -
created_at和updated_at不会自动生成,得用now()或Carbon::now()手动塞进去 - 字段名必须和数据库列名一字不差,大小写敏感(尤其 PostgreSQL)
- 示例:
DB::table('products')->insert(collect($products)->map(fn($p) => array_merge($p, ['created_at' => now(), 'updated_at' => now()]))->toArray());
批量赋值用 createMany() 是性能陷阱,除非你真需要模型事件
如果只是把用户提交的数组存进库,createMany() 是最慢的选择:它为每条数据 new 一个模型实例、触发 creating、执行 fill()、再 save()。1000 条数据可能吃光内存或超时。
- ✅ 正确场景:
createMany()只在你需要密码哈希、UUID 生成、自动填充访问器(如setPasswordAttribute)时才值得用 - ❌ 错误用法:把原始
request()->get('products')直接丢给Product::createMany(),没设$fillable会静默失败,设了又慢 - 更稳妥的做法:先用
collect($products)->map()清洗字段、补时间戳、做必要转换,再交给insert()
大数量必须分块,500 行是 MySQL 和 PDO 的安全阈值
哪怕数据格式全对,一次性插 5000 行也大概率触发 PDOException: SQLSTATE[HY000]: General error: 1390 Prepared statement contains too many placeholders。这不是 Laravel 限制,而是 MySQL 的 max_allowed_packet 和 PDO 协议层对占位符数量的硬约束。
- 推荐 chunk 大小:500 行/批,用
collect($data)->chunk(500)切分 - 不要依赖事务包住全部数据——大事务锁表时间长,失败回滚代价高
- 如果数据量超 10 万,直接切到 Artisan 命令行里跑,避开 Web 请求的
memory_limit和max_execution_time
字段映射、时间戳补全、chunk 切分这三步漏掉任何一环,批量入库就容易卡在中间状态——不是报错,而是部分成功、部分静默丢失,查起来反而更费时间。


















