hyperf 的 where()->update() 不触发模型事件是设计使然,因其直接走查询构造器拼 sql,不实例化模型,跳过 boot()、updating 等全部生命周期;需事件时应改用模型实例 $model->update()。

Hyperf 的 where()->update() 不触发模型事件,这是设计使然,不是 bug —— 它根本没走模型实例化流程,连 updating 都不会调用。
为什么 where()->update() 完全不走模型生命周期
Hyperf(和 Laravel 一样)的 update() 静态方法是查询构造器层面的操作,底层直接拼 SQL 执行,跳过整个模型层:
- 不创建模型实例 →
boot()、creating/updating等事件全部失效 - 不经过
fillable/guarded校验 → 字段写入完全裸奔,需自行确保安全性 - 不执行
casts转换 → JSON 字段若传数组,必须手动json_encode(),否则存成字符串Array - 不更新时间戳 →
updated_at不变,除非显式写进 update 数组里
where()->update() 和模型实例 $model->update() 怎么选
看你要不要模型层的“保障”或“副作用”:
- 要自动填充时间戳、软删除字段、mutator(如
setPasswordAttribute)、事件通知 → 必须用实例方法:$user->update(['name' => 'Alice']) - 只改一两个字段、无业务逻辑依赖、追求性能(尤其批量)→ 用静态方法:
User::where('status', 'pending')->update(['status' => 'processed']) - JSON 字段更新要特别小心:静态方法不走
casts,必须自己json_encode();实例方法会自动处理
Hyperf 下 JSON 字段批量更新的典型翻车点
常见错误写法(字段存成字符串 Array):
User::where('id', 1)->update(['preferences' => ['theme' => 'dark']]);
正确写法(手动序列化):
User::where('id', 1)->update(['preferences' => json_encode(['theme' => 'dark'])]);
或者退回到模型实例方式(更安全,但有开销):
$user = User::find(1);<br>$user->preferences['theme'] = 'dark'; // ⚠️ 错!不触发修改器<br>$user->preferences = array_merge($user->preferences, ['theme' => 'dark']); // ✅ 正确赋值<br>$user->save();
想保留事件又需要批量性能?别硬扛
没有银弹,但有折中路径:
- 小批量(collect(...)->each() + 实例
update(),事件可控,性能尚可 - 大批量且强依赖事件:拆成事务块 + 每块内用实例更新,避免单次事务过大
- 纯数据同步类场景:放弃事件,改用消息队列(如 Hyperf 的
AsyncTask)在更新后异步补发通知 - 绝对不要自己封装
disableEvents()—— Hyperf 没这玩意,withoutEvents()是 Laravel 的,Hyperf 不兼容
最易被忽略的一点:Hyperf 中即使你用了模型实例 update(),如果该模型没被注解扫描到(比如目录没加进 annotations.php 的 scan.paths),事件监听器照样不会注册——先确认 ReflectionManager::getAllClasses() 能列出你的监听器类。











