observer未触发的首要原因是未注册,需在appserviceprovider::boot()中调用model::observe(observerclass::class);其次注意created()与saved()等事件触发时机差异,避免逻辑错误或死循环。

Observer类没被触发?先确认是否已注册
Laravel的Observer不会自动生效,必须手动注册到模型上,这是最常被跳过的一步。不注册就监听不到任何事件,代码写得再全也没用。
- 在
AppServiceProvider::boot()里调用Model::observe(ObserverClass::class),比如User::observe(UserObserver::class) - 也可以在模型自身的
boot()方法里注册,但不推荐——耦合高、不易测试、批量注册时难管理 - 如果用了 Laravel 10+ 的自动发现(
php artisan event:discover),需确保EventServiceProvider中启用了$listen自动扫描,并且 Observer 类命名和路径符合约定(如App\Observers\UserObserver)
created() 和 saved() 行为不同,别混用
created() 只在 INSERT 成功后触发,而 saved() 在 INSERT 和 UPDATE 后都会触发。选错会导致逻辑重复执行或漏执行。
- 需要“仅首次创建时发通知”,用
created();要“每次保存都更新缓存”,用saved() -
updated()和saved()不是互斥关系:一次 update 操作会先触发updated(),再触发saved() - 注意:软删除场景下,
deleted()和forceDeleted()是分开的,delete()默认只软删,不会进forceDeleted()
Observer里不能直接改模型属性再 save(),会死循环
在 saving() 或 updating() 里调用 $model->save(),会再次触发相同事件,最终爆栈或超时。
- 想修改字段值,直接赋值即可:
$model->slug = Str::slug($model->name),Laravel 会在后续流程中自动保存 - 如果必须触发另一次保存(比如级联更新关联模型),改用
DB::transaction()+ 原生查询,或把逻辑抽到 service 层,在 observer 外调用 - 调试时可在 observer 方法开头加
Log::debug('user saving', ['id' => $model->id]),快速确认是否重复进入
队列化 Observer 要小心事务边界
Observer 方法默认同步执行,一旦开启队列(比如用 dispatch(new UpdateSearchIndex($model))),可能遇到模型数据已变或事务未提交的问题。
- 不要在 observer 里 dispatch 依赖当前模型完整状态的任务,除非你明确处理了延迟消费时的数据一致性
- 更稳妥的做法:在
created()等事件里 dispatch 一个带模型 ID 和关键字段的 job,然后在 job handle 中重新查询最新数据 - 如果任务必须等事务提交后再执行(比如发 webhook 通知外部系统),要用
DB::afterCommit()包裹 dispatch,否则可能拿到脏数据或失败回滚










