observer 必须手动注册,推荐在 appserviceprovider::boot() 末尾调用 user::observe(userobserver::class),laravel 不自动加载;created 仅 insert 后触发,updated 需主键存在且字段实际变更;批量更新不触发事件;dispatch 队列需 aftercommit();retrieved 易引发 n+1,方法名须严格小写匹配。

Observer 必须手动注册,不注册等于没写
观察者类放在 app/Observers/ 下、命名再规范,Laravel 5.5+ 也不会自动加载它。不显式调用 User::observe(UserObserver::class),事件永远不会进入回调。
- 注册必须在模型类已加载之后执行,否则静默失效;推荐在
AppServiceProvider::boot()末尾或使用use App\Models\User显式引入保证加载 - 不能写成
observe(new UserObserver)或传数组(除非明确要注册多个),正确写法只有User::observe(UserObserver::class) - 若模型通过字符串引用(如
'User'),需确保自动加载器能解析该类名,否则报Class 'User' not found - Laravel 9+ 可改用
EventServiceProvider::$observe集中注册,更安全且避免时机问题
created 和 updated 触发条件完全不同
created 只在 INSERT 成功后触发,updated 则要求:已有主键 + 字段实际变更($model->isDirty() 返回 true)。很多人误用 updated 处理新建逻辑,结果新建时完全不走。
-
saving/updating在写入前触发,可返回false中断保存;saved/updated已落库,改字段无效 -
saved在 insert 和 update 后都触发;updated仅对 UPDATE SQL 生效,firstOrCreate的 insert 分支不触发它 - 批量更新如
User::where()->update([])不触发任何模型事件,包括updated - 判断字段是否真变了?必须手动加
if ($model->isDirty('email')) { },Eloquent 默认不做 dirty check
Observer 里 dispatch 队列必须用 afterCommit()
观察者是同步执行的,直接在 created 里调 dispatch() 会导致队列进程读不到事务未提交的新数据。
- 正确姿势:
SendWelcomeEmail::dispatch($user)->afterCommit(),确保事务提交后再执行 - 避免在
saving或updating中 dispatch 队列——这些钩子可能被多次触发(比如循环 save),导致重复任务 - 若需读取关联数据(如
$user->profile),先在观察者里用$user->load('profile')预加载,别等队列里再查 - 观察者无请求上下文,不能依赖
$this->app或 Facade 获取当前 request,它可能运行在命令行、队列或 HTTP 请求中任意环境
软删除和 retrieved 是最容易踩坑的两个点
软删除触发的是 deleting/deleted,不是 forceDeleting;而 retrieved 每次 get()、first()、paginate() 都会触发,极易引发 N+1 或缓存击穿。
- 恢复软删用
restoring/restored,不是updating或updated -
retrieved适合做懒加载拦截或审计标记,但绝不适合放耗时操作(如 HTTP 调用、DB 查询) - 方法名必须严格小写且匹配事件名:
created✅,Created/onCreated/handleCreated❌ - 方法参数只能是模型实例,Laravel 不传第二个参数(如事件名),多加参数会导致调用失败且无报错











