必须在服务提供者boot()中调用model::observe()显式注册观察者;created在事务提交后触发,适合发通知;updated仅字段变更时触发,saved则每次保存都触发;关联模型需预加载或手动查询。

怎么注册模型观察者(Observer)
Laravel 的模型观察者不是自动生效的,必须显式注册。常见错误是写了 Observer 类但没绑定到模型,结果事件完全不触发。
- 在服务提供者(如
AppServiceProvider)的boot()方法里调用Model::observe(ObserverClass::class) - 观察者类必须继承
Illuminate\Database\Eloquent\Observers\Observer或直接定义静态方法(Laravel 9+ 支持无继承写法) - 不要放在
register()里 —— 此时模型尚未加载,会报Class 'App\Models\User' not found
// App\Observers\UserObserver.php
class UserObserver
{
public function created(User $user)
{
// $user 已存入数据库,主键已生成
}
<pre class='brush:php;toolbar:false;'>public function updating(User $user)
{
// 注意:此时 $user->isDirty('email') 可用,但 $user->getOriginal('email') 是旧值
}}
created 和 creating 哪个更适合发通知?
creating 在写入数据库前触发,created 在事务提交后触发。选错会导致数据不一致或通知内容缺失。
- 发短信、邮件、写日志等「依赖已持久化数据」的操作,必须用
created—— 否则可能取不到自增 ID 或关联数据 - 需要修改即将插入的数据(比如设置默认头像 URL),用
creating;但注意它不能抛异常中断保存(除非手动 throw) - 如果用了数据库事务且观察者里做了 DB 操作,
created更安全 —— 它在事务外执行(Laravel 10+ 默认行为),避免锁表或死锁
为什么 updated 不触发,但 saved 却能捕获?
updated 只在模型「实际字段被修改」时触发,而 saved 在每次成功保存(insert/update)后都触发。
-
$user->name = 'a'; $user->save();→ 触发updated -
$user->touch();或只改了时间戳字段 → 不触发updated,但触发saved -
$user->increment('views');→ 不触发updated(底层走的是 raw query,绕过模型事件) - 想监听所有变更,统一用
saved,但需自行判断:if ($user->wasChanged()) { ... }
Observer 里访问关联模型为什么总是 null?
观察者方法接收的是当前模型实例,它默认不加载关联关系。常见错误是直接调用 $user->posts 然后报错。
- 关联数据未预加载时,
$user->posts返回空集合或触发懒加载 —— 但观察者执行时可能已脱离请求上下文,懒加载失败 - 解决办法只有两个:在触发事件前显式预加载(如
User::with('posts')->find($id)),或在观察者里用查询重建(Post::where('user_id', $user->id)->get()) - 更稳妥的做法是:把需要的字段提前塞进模型,比如在
saving里用$user->setAttribute('last_post_title', ...),然后在saved里读取
模型事件本质是「钩子」,不是魔法。它不改变 Eloquent 的生命周期阶段,也不自动补全数据。什么时候查、查什么、要不要加事务,都得自己想清楚。










