Laravel如何利用Observer监听模型事件?(代码解耦)

3次阅读

observer类不必继承modelobserver;laravel仅要求方法名匹配事件名(如created)、参数为对应模型实例,且注册时用booted更安全,因依赖已就绪;saving可中断事务,saved则确保数据已落库。

Laravel如何利用Observer监听模型事件?(代码解耦)

Observer类必须继承 ModelObserver 吗?

不用。Laravel 的 Observer 本质是普通 PHP 类,只要方法名匹配模型事件(如 createdupdated),就能被自动调用。框架不校验父类,也不强制实现接口。写成 class UserObserver 空类完全合法。

常见错误现象:有人照着旧文档加了 extends ModelObserver,结果报 Class 'AppObserversModelObserver' not found——因为这个类根本不存在于 Laravel 10+ 中。

实操建议:

  • 直接定义普通类,比如 app/Observers/UserObserver.php
  • 方法名严格对应事件名:一个模型新增后触发 created(),不是 onCreated()handleCreated()
  • 方法参数必须是对应模型实例,例如 public function created(User $user),类型提示不能错

注册 Observer 时 bootedbooting 哪个时机更安全?

booted。模型的静态 booting 钩子在类加载时就执行,此时服务容器可能还没完全就绪;而 booted 在第一次被解析后触发,能确保依赖(如日志、队列、DB)可用。

使用场景:你在 Observer 里调用了 Log::info() 或 dispatch 了一个 job,如果注册在 booting,可能因日志通道未初始化而静默失败。

实操建议:

  • AppServiceProvider::boot() 里注册:User::observe(UserObserver::class)
  • 不要在模型自身的 boot() 方法里调用 observe(),容易循环依赖或重复绑定
  • 多个 Observer 按需注册,别一股脑全塞进一个 observe() 调用里

savingsaved 的区别不只是“前后”那么简单

区别在于事务上下文和可中断性。saving 在事务内、可返回 false 中断保存;saved 在事务提交后触发,无法阻止写入,但能确保数据已落库。

性能影响:在 saving 里做耗时操作(如远程 API 调用)会拖慢整个事务,还可能因超时导致数据库锁等待;saved 更适合发通知、写日志、触发异步任务。

实操建议:

  • 需要校验/修改即将入库的数据 → 用 saving,但只做轻量逻辑
  • 要发邮件、推消息、更新搜索索引 → 用 saved,并配合 dispatch(new SendWelcomeEmail($user))
  • 避免在 saving 里调用 $model->save(),会造成无限递归

Observer 里访问 $model->getOriginal() 总是空?

因为 created 事件中,模型刚从数据库取回,originalattributes 是一致的;而 updated 事件里,getOriginal() 才真正反映变更前的值——但前提是模型开启了脏检查(默认开启)且字段确实变了。

容易踩的坑:有人在 updated 里直接比对 $model->name !== $model->getOriginal('name'),却发现恒为 false。原因可能是模型没设置 $fillable,或者更新时用了 update(['name' => 'x']) 但没触发模型事件(比如绕过了 Eloquent,用了 Query Builder)。

实操建议:

  • 确认更新操作走的是模型方法:$user->update(...)$user->save(),而不是 DB::table()->where()->update()
  • 调试时打印 $model->isDirty()$model->getChanges(),看是否真有变更
  • 不要依赖 getOriginal() 判断“是否首次设置”,它不反映业务意义上的“空值转非空”,只反映 Eloquent 记录的上一次状态

事情说清了就结束。Observer 看似简单,但事务时机、脏数据判断、注册生命周期这几个点,一不留神就会让监听失效或行为诡异。

text=ZqhQzanResources