observer 必须手动注册到模型且时机正确:在 appserviceprovider::boot() 中调用 model::observe(observer::class),或在模型的 booted() 静态方法中定义事件逻辑;saving 可中断保存并修改数据,saved 在事务提交后执行;批量更新不触发模型事件。

Observer 类写好了但事件不触发?八成是没注册,或者注册时机错了。Laravel 从不自动加载 Observer,必须手动绑定到模型,且得在模型已就绪之后执行。
Observer 必须显式注册,否则等于没写
生成 UserObserver 后,app/Observers/UserObserver.php 文件存在 ≠ 它会生效。Laravel 不扫描目录、不自动发现类。
- 正确做法:在
AppServiceProvider::boot()中调用User::observe(UserObserver::class) - 错误做法:只放文件、不注册;或在
EventServiceProvider的$listen里配(那是给事件系统用的,不是 Observer) - 如果模型类名是
Post,Observer 类名必须是PostObserver,否则 IDE 可能报错,且observe()内部反射会失败
注册时机选 booted() 还是 boot()?
Laravel 10+(及新版 9.x)中,模型事件监听逻辑已移到实例化后的 booted() 钩子,boot() 仅用于静态初始化,放事件监听器不会触发。
-
booted()是 protected static function,必须定义在模型类内部,例如:protected static function booted() { static::created(fn ($user) => Log::debug('User created: '.$user->id)); } - 不要在
boot()里写static::created(...),它不会执行 - 若用 trait 复用逻辑,确保
booted()没被覆盖,必要时加parent::booted()
saving 和 saved 别混用,事务行为完全不同
这两个钩子看似只差一个字母,但执行上下文和可靠性差异极大:
-
saving在事务内、INSERT/UPDATE 前触发,可修改字段值或返回false中断保存(比如校验必填字段) -
saved在事务提交后触发,数据已落库,适合发通知、写日志、触发队列任务 - 在
saving里调$model->save()会递归触发,导致爆栈;在saved里改属性再save()也无效(已入库) - 批量更新如
User::where(...)->update(...)不触发任何模型事件,Observer 对它完全无感
软删除、检索、脏检查这些细节容易踩坑
Observer 方法名必须严格匹配,大小写、拼写、是否带 force 前缀都不能错:
- 软删走
deleting→deleted;硬删才走forceDeleting→forceDeleted -
retrieved会在每次find()、get()、paginate()时为每个模型实例触发一次,高频查询下易打满日志 - 判断字段是否真变了,不能只看值,得用
$model->isDirty('email');想取原始数据库值,用$model->getOriginal('status') -
updated()不保证字段有变化——如果模型属性没变,isDirty()返回 false,updated()就不会触发











