模型事件仅在eloquent实例操作(如save、create)时触发,绕过eloquent的db查询或批量方法(如insert、upsert)完全不触发;boot()适合简单逻辑,观察者适合复杂业务;saving/saved通用,updating/updated仅在字段实际变更(isdirty为true)时触发;软删除需显式监听restoring/restored。

模型事件不会自动触发,必须通过 Eloquent 实例的 save()、create()、update()、delete() 等方法调用才会激活;绕过 Eloquent 的操作(如 DB::table()->insert() 或 upsert())完全不触发任何模型事件。
模型事件只在 Eloquent 实例操作时触发
这是最常被忽略的前提。Laravel 的模型事件本质是 Eloquent 生命周期钩子,不是数据库层监听器。
-
creating、created等事件仅在$model->save()或User::create(...)这类走完整模型流程的操作中触发 -
DB::table('users')->insert([...])、User::upsert(...)、Model::insert([...])均跳过模型逻辑,事件静默失效 - 事务内直接执行原生 SQL 或使用 Query Builder 写入,同样不会触发 —— 即使你写了
static::created(...)回调也毫无反应 - 若必须批量写入且需事件语义,只能循环构造模型实例:
collect($data)->each(fn($d) => User::create($d)),或手动补全生命周期(不推荐)
boot() 中注册 vs 观察者(Observer)怎么选
两者都能监听,但适用边界清晰:简单副作用用 boot(),复杂逻辑必须用观察者。
-
boot()适合单行逻辑,比如自动设置$model->slug或记录创建时间戳:static::creating(fn($m) => $m->slug = Str::slug($m->name)) - 观察者适合含业务判断、外部调用、多步操作的场景,例如「用户更新邮箱后发验证邮件 + 同步到 CRM + 清除旧 token」
- 观察者类需通过
php artisan make:observer UserObserver --model=User生成,并在AppServiceProvider::boot()中注册:User::observe(UserObserver::class) - 别把耗时操作(如 HTTP 请求、文件写入)塞进
boot()回调 —— 它们同步执行,会拖慢主请求
saving/saved 和 updating/updated 的关键区别
混淆这四者是调试失败事件的最常见原因,核心差异在于「是否已判定为更新操作」以及「是否真的发生了字段变更」。
-
saving和saved是通用钩子:对新建和更新都触发,不管模型有没有实际改动 -
updating和updated是窄口径钩子:仅当$model->isDirty()返回 true(即有字段值变化)时才触发 - 举例:
$user->name = 'Alice'; $user->save();→ 触发saving、updating、updated、saved - 再例:
$user->touch(); $user->save();(仅更新时间戳)→ 触发saving、saved,但不触发updating/updated,因为isDirty()为 false
软删除与 restoring/restored 事件容易漏掉
软删除不是物理删除,deleting/deleted 默认不触发;必须显式监听 restoring/restored 才能捕获恢复动作。
-
$user->delete()→ 触发deleting、deleted(注意:这是软删,数据仍在库中) -
$user->restore()→ 触发restoring、restored,而非updating/updated - 若模型未声明
use SoftDeletes;,则restore()方法不存在,调用会报错Call to undefined method - 物理删除需用
$user->forceDelete(),此时才触发forceDeleted(Laravel 10+)或传统deleting/deleted
真正难处理的不是“怎么写”,而是「什么时候没触发」——多数问题出在操作方式偏离了 Eloquent 实例路径,或者误判了 isDirty() 的行为。盯住你的数据是怎么进库的,比盯住事件回调本身更重要。











