模型事件监听必须定义在Eloquent模型类的boot()方法中或通过服务提供者全局注册,写在控制器等处无效;creating在插入前可修改属性但无ID,created在插入后有ID但改属性不自动保存。

模型事件监听写在哪?不是在控制器里
模型事件监听必须定义在 Eloquent 模型类内部,或通过服务提供者全局注册。写在控制器、中间件或路由闭包里完全无效——事件根本不会触发。
-
static::creating()、static::saved()这类静态方法调用,必须放在模型类的boot()方法中,且只能在模型被加载后执行(比如首次引用该模型时) - 如果用服务提供者注册(如
AppServiceProvider),需确保在boot()里调用,且模型类已自动加载(否则会报Class not found) - 别在模型构造函数里注册事件——此时模型还没初始化完成,
static::调用会失败
creating 和 created 的区别到底在哪?
这两个事件都发生在插入数据库前/后,但关键差异在于事务状态和数据可用性:creating 是“即将插入但还没进 DB”,created 是“刚从 DB 返回、已带主键 ID”。
-
creating中可以修改属性(如$model->slug = Str::slug($model->name)),这些修改会进入 INSERT 语句;但不能访问$model->id(因为还没生成) -
created中可以安全调用$model->id、触发关联操作(如新建日志记录),但此时再改属性不会写回数据库(除非显式调用save()) - 若使用
DB::transaction(),creating在事务内,created也在同一事务中;但saving/saved同理,而deleting不等同于deleted:前者可 abort 删除(return false),后者不可逆
为什么事件没触发?常见断点检查清单
90% 的“监听不生效”问题出在加载时机或条件判断上,而不是语法错误。
- 模型没继承
Illuminate\Database\Eloquent\Model—— 比如用了Model别名但忘了use,会导致static::调用静默失败 - 事件注册写在了
__construct()或普通方法里,而非boot();或者boot()被重写但没调用parent::boot()(Laravel 9+ 默认不强制要求,但自定义静态初始化逻辑时容易漏) - 用了
updateOrCreate()或upsert():它们**不触发**creating/updating,只触发saved(因为底层走的是 SQL UPDATE,非 Eloquent 生命周期) - 批量操作如
User::insert()完全绕过模型事件——它直接执行 SQL,连模型实例都不创建
监听器里能干啥?哪些操作要特别小心
监听器本质是同步回调,所有代码都在当前请求生命周期内执行,没有队列兜底。
- 避免在
created里调用耗时外部 API(如发邮件、调第三方 webhook)——会拖慢响应,且失败无重试;应 dispatch 一个Job异步处理 - 不要在
saving里做数据库查询(比如根据 category_id 查分类名再塞进字段)——可能引发 N+1,更糟的是在事务中查未提交的数据,导致脏读 - 监听器里抛出异常会中断整个保存流程(包括事务回滚),这有时是预期行为(如校验失败),但别依赖它做业务逻辑分支——用显式
if+return false更清晰 -
retrieved事件适合做属性转换(如解密字段),但它在每次get()、first()、甚至关系加载时都触发,高频场景下注意性能
事情说清了就结束。模型事件不是钩子集合,它是 Eloquent 生命周期的精确切片——错一个阶段,逻辑就滑到另一个世界。










