Observer方法里调用DB::transaction无效,因Observer回调不自动加入外层事务上下文,且Laravel不支持嵌套事务;正确做法是将强一致性逻辑提前纳入主事务闭包或改用Event+Listener配合手动事务控制。

Observer 方法里调用 DB::transaction 为什么没用
因为 Observer 回调本身不自动加入外层事务上下文。哪怕你在控制器里写了 DB::transaction,一旦模型保存完成、Observer 的 created 或 updated 被调用,它已经处于事务提交之后的独立执行阶段。
- 事务边界只包裹模型 save/create 操作本身,不延伸到 Observer 回调
- 如果 Observer 里再写
DB::transaction,属于嵌套事务,Laravel 底层不支持真正的嵌套,DB::rollBack()可能只回滚内层,外层已提交的数据无法撤回 - 常见症状:主模型插入成功,Observer 里关联表写入失败,但主模型没回滚
正确做法是把整个流程(含 Observer 中必须强一致的逻辑)提前收进事务闭包里,或者改用事件系统配合手动事务控制。
什么时候该用 Observer,什么时候该用 Event + Listener
Observer 是模型专属、轻量、同步的钩子;Event 是跨模型、可队列、可广播、生命周期更可控的解耦机制。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 用 Observer:缓存清理(
Cache::forget('user_'.$user->id))、日志记录(Log::info())、字段自动填充($user->last_login_at = now())这类简单、低延迟、不依赖其他模型的操作 - 用 Event:需要发邮件、调用外部 API、更新多个无关模型、或必须保证与主操作原子性一致(比如订单创建 + 库存扣减),这时应触发
OrderCreated事件,在监听器里用DB::transaction包裹全部写操作 - Observer 不能监听
saving阶段的验证失败后行为,而事件可在Model::creating中提前拦截并抛异常
Observer 注册后不触发的三个硬性条件
Observer 不是“生成即生效”,它依赖三个不可跳过的加载时机和命名约定。
- 模型类必须已加载且完成静态初始化——在
AppServiceProvider::boot()中注册前,确保User::class已被 PHP 加载(否则User::observe()会静默失败) - Observer 方法名必须全小写且严格匹配事件名:
created、updated、retrieved,不能写成Created或onCreated - Laravel 10+ 版本中,若模型使用了
$dispatchesEvents静态数组(如['created' => UserCreated::class]),则 Observer 的同名方法会被跳过——两者互斥,不能混用
retrieved 事件里做日志,为什么每次请求都打两遍
这是 Eloquent 的懒加载 + N+1 查询典型副作用。retrieved 在每次模型从数据库取出时都会触发,包括通过关系访问时隐式加载的子模型。
- 例如
User::with('posts')->find(1):父模型User触发一次retrieved,每个Post实例又各自触发一次 - 如果
Post模型也注册了同名 Observer,日志量会指数级增长 - 解决办法不是禁用
retrieved,而是加守卫:在 Observer 方法里判断是否为顶层查询,比如检查$user->wasRecentlyCreated === false && $user->getAttribute('_retrieved_from_top_level') === true,然后由上层主动设置该属性
真正难处理的是跨模型观察和递归加载场景——这里没有银弹,得靠明确的加载意图标记和分层日志策略。

















