
当 Laravel 模型中已正确声明 $fillable,但 update() 无法更新指定字段(如 text)时,往往因模型属性未被标记为“已变更”,导致 Eloquent 跳过更新;此时应改用 save() 主动触发保存逻辑。
当 laravel 模型中已正确声明 `$fillable`,但 `update()` 无法更新指定字段(如 `text`)时,往往因模型属性未被标记为“已变更”,导致 eloquent 跳过更新;此时应改用 `save()` 主动触发保存逻辑。
在 Laravel 中,update() 和 save() 表面相似,但底层行为有本质区别:
- update() 是静态批量更新方法:它直接执行 SQL UPDATE 语句,不加载模型实例,也不触发模型事件、访问器、修改器或验证逻辑。它仅接受关联数组(如 ['text' => $value]),并依据主键更新数据库。
- save() 是实例方法:它作用于已存在的模型实例,会检查 $changes 属性(即哪些字段被显式赋值且值发生变更),仅将变更字段写入数据库,并完整触发模型生命周期(如 updating 事件、setXXXAttribute 访问器等)。
回到问题代码:
$assigned = PerformanceManagementSetModuleStandardAssignedPlanning::where('id', $p['id'])->first();
$assigned->text = $p['text']; // ✅ 属性已赋值
$assigned->update(); // ❌ 错误:update() 不读取实例属性,而是等待传参!
此处调用 update() 时未传入任何数据,Eloquent 会默认尝试更新空数组 —— 实际执行的是 UPDATE ... SET updated_at = NOW(),不会更新 text 字段,因此看似“无效”。
而 save() 正确利用了 Eloquent 的变更跟踪机制:
$assigned->text = $p['text']; $assigned->user_id = 1; $assigned->save(); // ✅ 自动识别 text 和 user_id 已变更,并更新对应字段
✅ 正确写法(推荐):
foreach ($request->assigned['standards'] as $standard) {
if (!empty($standard['plannings'])) {
foreach ($standard['plannings'] as $p) {
$assigned = PerformanceManagementSetModuleStandardAssignedPlanning::where('id', $p['id'])
->where('performance_management_set_assigned_id', $request->assigned_id)
->first();
if ($assigned) {
$assigned->text = $p['text'];
$assigned->user_id = 1;
$assigned->save(); // 使用 save() 替代 update()
}
}
}
}
⚠️ 注意事项:
- 避免在循环中频繁查询和保存(N+1 问题)。如需高性能批量更新,可改用 upsert() 或原生 DB::table()->update(),但会牺牲模型逻辑;
- 确保 $fillable 声明准确(本例中 text 已包含,无问题);
- dump($assigned) 显示 "text" => null 在 #original 中,说明该字段原始值为 NULL,而新赋值非 NULL,save() 能正确检测此变更;
- 切勿依赖 @ 抑制错误(如 @$standard['plannings']),应使用 isset() 或 filled() 进行健壮判断。
? 总结:
当通过模型实例修改属性后需持久化,请始终使用 save();若需无模型实例的轻量更新,才应使用静态 update() 并显式传入数据数组(如 ::where(...)->update(['text' => $p['text']]))。二者不可混用——这是 Laravel 数据持久化中最易被忽视的关键分界点。











