Laravel 8 Eloquent调用where()后update()初始条件丢失问题
Laravel 8 Eloquent 调用update丢失主键条件问题说明
这不是框架本身的Bug,属于对Eloquent方法调用逻辑理解偏差导致的用法错误。
核心原因
Post::find($id)执行时就已经完成了一次数据库查询,返回的是已装配主键属性、携带对应行数据的模型实例,不是待构建的查询构造器对象。- 直接在模型实例上调用
update()时,走的是模型类自带的单记录更新逻辑:方法内部会自动追加当前实例的主键作为查询条件,同时会触发模型生命周期事件、自动维护updated_at时间戳字段,因此无额外where的场景可以生成带id约束的正确SQL。 - 在已获取的模型实例上调用
where()方法时,会触发模型的魔术调用逻辑,生成一个全新的、独立的查询构造器实例,这个新实例和之前find()的查询条件没有任何关联,不会自动携带当前模型的主键约束。此时后续调用的update()是查询构造器提供的批量更新方法,而非模型实例的单记录更新方法,只会执行你在这个新构造器上设置的where条件,自然会丢失初始的主键条件。同时这种批量更新不会触发模型事件,也不会自动维护时间戳字段。
正确实现方式
根据业务场景选择对应写法即可:
- 需要单模型更新能力(触发事件、自动维护时间戳):在查询阶段就拼接所有约束条件,匹配到对应模型后再执行更新
// 查询时就带上所有筛选条件,匹配不到记录直接返回null $post = Post::where('id', $id) ->where('status', 'unpublished') ->first(); if ($post) { $post->update(['status' => 'published']); }
- 不需要模型事件、追求更新效率:直接通过查询构造器写全所有条件执行批量更新
Post::where('id', $id) ->where('status', 'unpublished') ->update(['status' => 'published']);
注意:上述批量更新写法不会触发模型的
saving/updating/updated等生命周期事件,也不会自动填充updated_at字段,相关字段值需要手动维护。
内容的提问来源于stack exchange,提问作者H Zafari
相关产品推荐
相关产品推荐

