模型事件监听必须用 booted() 而非 boot(),因 Laravel 10+ 将事件注册移至实例化后的 booted();saved() 在增/改时均触发,updated() 仅在真实更新且有变更时触发;批量 update 不触发模型事件,通配监听需谨慎使用。

模型事件监听必须用 booted() 而不是 boot()
很多人在模型里写 boot() 静态方法,试图在里面调用 creating()、saving() 等,结果事件完全不触发——因为 Laravel 10+(及较新版本的 9.x)已将模型事件监听逻辑移到实例化后的 booted() 回调中,boot() 只负责静态初始化。
实操建议:
-
booted()是实例方法,必须在模型类中定义为protected static function booted() - 所有事件监听器(如
created()、updated())必须在booted()内注册,不能放外面或放在boot() - 如果用了 trait 或父类复用逻辑,确保
booted()不被覆盖,可用parent::booted()显式调用
protected static function booted()
{
static::created(function ($user) {
// 这里能正常触发
\Log::info('User created: '.$user->id);
});
static::updated(function ($user) {
// 注意:updated 不包含 firstOrCreate 场景,它只对 update() 和 save() 触发
});
}
Observer 类里 saved() 和 updated() 的区别很关键
新手常以为 saved() 就是“保存后”,但其实它在 insert 和 update 两种场景下都会触发;而 updated() 只在真正执行了 UPDATE SQL 时才触发(即已有主键、且数据有变更)。
常见错误现象:
- 用
updated()处理“新建后初始化关联数据”,结果新建用户时根本没走这个回调 - 在
saved()里无条件调用$model->refresh(),导致循环 save(尤其配合自动填充字段时) - 误以为
updated()能捕获所有字段变化,实际它不感知是否真的改了值(Laravel 不做 dirty check,除非你手动$model->isDirty('email'))
使用场景建议:
- 需要区分新建 or 更新?用
created()+updated()组合,别全压给saved() - 要确保字段确实变了再行动?在
updated()或saved()里加if ($model->isDirty('status')) { ... } - 避免在 observer 里调用
$model->save()或$model->touch(),极易引发递归或死循环
全局监听要用 Event::listen(),但注意优先级和性能
如果你不想每个模型都写 booted(),而是想统一处理比如「所有模型更新时记录操作日志」,就得用事件系统。但直接监听 eloquent.updated: * 这类通配事件,容易踩坑。
实操要点:
- 通配监听语法是
eloquent.updated: App\Models\User(精确到类)或eloquent.updated: *(全部),但后者会监听 所有 模型,包括软删除恢复、批量更新等隐式操作 - 在
EventServiceProvider::boot()中注册,不要在中间件或请求生命周期中动态 listen,否则可能重复绑定 - 批量操作(如
User::where(...)->update([...]))不会触发任何模型事件,只会触发eloquent.updating: *(注意是 updating,不是 updated),且没有模型实例传入——这点极易被忽略 - 高频写入场景下,通配监听会带来明显开销,建议按需订阅具体模型,或用队列延迟处理日志
Observer 类注册后不生效?检查 AppServiceProvider 是否漏掉了 observe()
自定义 Observer 类(如 UserObserver)必须显式注册,否则连构造函数都不会执行。Laravel 不会自动扫描或加载 observer。
常见错误:
- 写了
UserObserver类,但没在AppServiceProvider::boot()里调用User::observe(UserObserver::class) - 在
boot()里写了observe(),但顺序错了——比如在Schema::defaultStringLength(191)之前,而 observer 里又依赖数据库连接 - 用的是多数据库连接,但
observe()注册时没指定连接,导致监听了错误连接上的模型
正确姿势:
- 推荐在对应模型的
booted()里注册 observer,更内聚:static::observe(UserObserver::class) - 若必须全局注册,请确保在
AppServiceProvider::boot()的末尾,且确认模型类已自动加载(必要时加class_exists(User::class)防错) - observer 构造函数里不要做重操作(如查库、发 HTTP 请求),它会在每次模型实例化时执行
boot() 还能绑事件、把 saved() 当成万能钩子、以及批量 update 完全不走 observer 却还指望它记日志。


















