thinkphp 6 的 update() 实例方法只能更新单条记录,批量改状态须用静态 model::update() 或 db::table()->update(),传条件数组或闭包,生成原生 update sql,避免查数据、性能差及静默失败。

ThinkPHP 6 的 update() 方法为什么不能直接批量改状态?
因为 update() 是模型实例方法,只作用于当前模型对象——你查出一条记录调一次 save(),它才改一条。想按条件批量更新(比如「把所有 status=0 的订单改成 status=1」),必须绕开单实例逻辑,用真正能下推到 SQL 的方式。
常见错误现象:
– 写 $order->where('status', 0)->update(['status' => 1]) 却没生效
– 或用了 select() + 循环 save(),结果慢、锁表、事务不稳
- 正确姿势是用模型的静态
update()(注意不是实例方法)或Db::table()->update() - ThinkPHP 6.0+ 中,
Model::update()支持传数组 + 条件参数,底层生成UPDATE ... WHERE,不查数据 - 别混用
where()链式调用后跟update():链式where返回的是 Query 对象,不是 Model,调update()会报错或静默失败
用 Model::update() 批量改状态的写法和坑点
这是最推荐的方式,语义清晰、走模型事件、支持软删除判断(如果开了)。
示例:
Order::update([
'status' => 1,
'updated_at' => time()
], ['status' => 0]);
- 第二个参数是
WHERE条件数组,不是闭包;闭包得用where()配合update()静态方法重载(见下条) - 字段值不会自动过滤非写入字段(比如
id、create_time),要确保传入的数组只含允许更新的字段 - 如果模型定义了
protected $type = ['updated_at' => 'timestamp'],time()会被自动转为格式化时间;否则得手动写date('Y-m-d H:i:s') - 返回值是影响行数(int),不是模型实例,别当成对象链式调用
复杂条件怎么办?用 Db::table() 或闭包条件
当条件涉及关联字段、子查询、或需要 IN、BETWEEN 等时,数组条件不够用。
两种等效写法:
– 用 Db::table('order')->where(...)->update([...])
– 或用模型静态方法加闭包:
Order::update([], function ($query) {
$query->where('status', 0)
->where('created_at', '
-
Db::table()绕过模型逻辑,更快但不触发saving/saved事件,也不走自动时间戳、类型转换 - 闭包写法里,第一个参数必须是空数组
[],否则 ThinkPHP 会误判为数据更新项 - 如果字段名含表达式(如
score = score + 10),只能用Db::raw():['score' => Db::raw('score + 10')]
事务里批量更新要注意什么?
批量更新本身是原子操作,但如果你在事务中混合了查询 + 更新,就容易踩坑。
- 别在事务里先
select()出 ID 列表再循环update():ID 多时性能差,且可能被其他事务修改导致状态不一致 - 推荐事务内直接用单条
Model::update()或Db::table()->update(),保证 WHERE 和 SET 同步下推 - 如果更新前必须校验业务规则(比如“只有未支付订单才能关闭”),建议用
SELECT ... FOR UPDATE加锁查出再更新,而不是靠应用层 if 判断——后者有竞态风险
真正难的不是语法,是判断该不该查出来再改;多数状态批量变更,查出来只是心理安慰,反而拖慢系统、增加死锁概率。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










