laravel模型事件在eloquent内存操作阶段触发而非数据库写入完成时:saving在sql执行前、saved在事务未提交时;creating/updating可修改属性但须返回true,created/updated内禁用save()防递归;推荐boot静态注册监听,避免构造函数或appserviceprovider中注册;多监听器按序执行无优先级,异常中断后续;软删除需显式启用deleting等事件,forcedeleted用于永久删除;批量操作绕过所有模型事件。

模型事件触发时机不明确,导致逻辑错位
Laravel 的模型事件(如 creating、saving、updated)不是在数据库写入完成那一刻才触发的——它们发生在 Eloquent 生命周期的内存操作阶段。比如 saving 在数据验证后、SQL 执行前调用;saved 则在 INSERT/UPDATE 成功执行后、事务尚未提交时触发。
-
creating和updating可以修改模型属性(如自动填充slug),但改完必须返回true,否则会中断保存流程 -
created和updated里不能再调用$model->save(),否则可能引发递归监听或死循环 - 如果用了数据库事务,
saved触发时事务还没 commit,此时做外部 HTTP 请求或写日志没问题,但别依赖「其他服务一定能查到这条记录」
监听器注册方式选错,事件根本没响应
模型事件监听有三种主流方式:静态 boot 方法内注册、服务提供者中全局绑定、或使用观察者类。最常踩的坑是把监听器写在模型构造函数里——它不会生效,因为事件系统不通过实例方法注册。
- 推荐用
boot静态方法,清晰、就近、无需额外类文件:protected static function boot() { parent::boot(); static::saving(fn ($model) => $model->slug = Str::slug($model->title)); } - 若逻辑复杂或需复用,用观察者类,但记得运行
php artisan make:observer PostObserver --model=Post并在EventServiceProvider中注册Post::observe(PostObserver::class) - 避免在
AppServiceProvider的boot里用Post::saving(...)—— 模型类可能还没被加载,导致监听静默失效
多个监听器执行顺序混乱,副作用难追踪
同一个事件(如 saving)可以绑定多个回调,它们按注册顺序执行,但没有内置优先级控制。一旦某个监听器抛出异常,后续监听器全部跳过,而模型保存也会中止。
- 不要在监听器里写长耗时操作(如发送邮件、调用第三方 API),应 dispatch job 异步处理
- 若需确保某逻辑最后执行(比如审计日志),把它放在
boot方法的末尾注册,或统一收口到一个观察者里再按需分发 - 调试时可在监听器开头加
Log::debug("saving fired for ".get_class($this));,配合php artisan tinker+Post::first()->save()快速验证触发链
软删除模型的事件容易漏掉关键钩子
软删除不是真删,所以 deleting 和 deleted 默认不会触发。必须显式启用 restoring / restored,并注意 forceDeleting 这种强制删除场景需要单独监听。
- 启用软删除事件需在模型中设置:
protected $dispatchesEvents = ['deleting' => PostDeleting::class];,否则即使写了static::deleting(...)也不会进 -
trashed()状态下模型仍可触发updating,但不能触发deleting—— 它只对未软删状态有效 - 想拦截「永久删除」动作,必须监听
forceDeleted,而不是deleted;前者对应Post::withTrashed()->where(...)->forceDelete()
Post::where(...)->update(...) 完全绕过所有模型事件),也不适合做跨模型强一致性校验——那种地方该交给数据库约束或领域服务协调。










