必须手动注册模型观察者才能生效,Laravel 不自动加载;creating 在入库前触发(id 为 null),created 在插入后、事务提交前触发(id 可用);updating 仅对模型实例 save/update 有效,不响应批量更新;观察者中禁用 HTTP 请求、嵌套事务及 forceDelete。

怎么注册模型观察者(Observer)
观察者必须先注册才能生效,不注册等于没写。Laravel 不会自动扫描或加载 Observer 类,得手动告诉它“这个类要监听哪个模型”。
常见错误:把 UserObserver 放在 app/Observers/ 下就以为完事了,结果 created、updated 一个都不触发。
- 用
boot方法在AppServiceProvider里注册:use App\Models\User; use App\Observers\UserObserver; public function boot() { User::observe(UserObserver::class); } - 也可以在模型自己的
boot方法里注册(适合单模型强耦合场景):protected static function boot() { parent::boot(); static::observe(UserObserver::class); } - 注意:注册顺序有影响——如果模型已存在静态事件监听(比如直接用了
static::created(...)),再调observe()可能被覆盖或后执行,建议统一用一种方式
creating 和 created 的区别到底在哪
这两个钩子都发生在插入时,但触发时机和事务状态完全不同,选错会导致数据查不到、事务回滚失败等问题。
-
creating:发生在模型刚实例化、还没进数据库前,$model->id还是null,可以改属性、加默认值、抛异常中断保存 -
created:INSERT SQL 已执行成功,主键已生成,事务还没 commit,此时能安全读取$model->id,但不能再改模型属性(改了也不会入库) - 典型踩坑:在
created里调$model->save()触发无限循环;在creating里依赖$model->id做逻辑,结果是null
为什么 updating 有时不触发
不是所有更新操作都会走 updating,Laravel 对批量更新、原生查询、update() 静态方法做了绕过处理。
- 触发的:用模型实例调
$user->name = 'x'; $user->save();或$user->update([...]); - 不触发的:
User::where('id', 1)->update(['name' => 'x']);、DB::table('users')->...、Model::upsert()(Laravel 9+) - 替代方案:如果必须用批量更新又需要监听,要么改用模型循环 +
save()(注意 N+1),要么在业务层显式调用自定义方法,别依赖观察者兜底 - 另外:
updating在模型属性没真正变化时也可能跳过(受$casts和$dates影响),可加日志打$model->isDirty()确认
观察者里不能做哪些事
观察者生命周期绑定在模型事件上,它的执行上下文很脆弱,容易引发死锁、内存溢出或事务异常。
- 别在观察者里发起 HTTP 请求(如调第三方 API)——超时或失败会卡住整个事务,且无法重试
- 避免在
deleting里调$model->forceDelete()或递归删关联模型,可能触发嵌套观察者,导致栈溢出 - 不要在
created或updated里用DB::transaction()嵌套——当前事件已在事务中,强行再开事务可能被忽略或报错SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active - 异步任务(如 dispatch job)可以做,但记得用
onQueue()显式指定队列,别依赖默认同步驱动
观察者不是万能钩子,它只对“模型实例的生命周期操作”有效。一旦跳出这个范围,比如直接 SQL、缓存刷新、搜索索引更新,就得靠事件系统(Event::dispatch())或应用层协调——这点最容易被忽略。










