ThinkPHP模型事件必须在模型类中通过$dispatchesEvents显式声明才生效,且需严格匹配内置事件名;观察者模式需手动调用Event::observe()绑定,否则不会触发。

ThinkPHP 模型事件怎么注册才生效
模型事件必须在模型类内部通过 protected $dispatchesEvents 显式声明,否则监听器完全不会触发。框架不会自动扫描或默认启用任何事件监听。
-
$dispatchesEvents是关联数组,键为事件名(如'created'、'updated'),值为对应监听器类的完整命名空间路径 - 事件名必须严格匹配框架内置名称:
'creating'(保存前)、'created'(保存后)、'updating'、'updated'、'deleting'、'deleted'、'retrieved'(查询后)等 - 监听器类需实现
Illuminate\Contracts\Queue\ShouldQueue接口才能异步执行,否则始终同步运行 - 如果模型继承自
think\Model而非 Laravel 风格的Illuminate\Database\Eloquent\Model,则原生 ThinkPHP 6 的事件机制不兼容 Laravel 风格监听器
用 Observer 类监听时为什么 never 被调用
ThinkPHP 官方文档里提的 “观察者模式” 实际是仿 Laravel 写法,但 TP6 默认未启用该机制 —— 必须手动调用 think\facade\Event::observe() 才会绑定,且仅对 think\Model 子类有效。
- Observer 类方法名必须小驼峰且与事件名一致:
created()、updated(),不能写成onCreated() - 必须在应用初始化阶段(如
app\common\event.php或服务提供者中)调用Event::observe(User::class, UserObserver::class) - 如果模型用了软删除(
use SoftDelete),'deleted'不会触发,要监听'forceDeleted'才能捕获真正删库操作 - 批量更新(如
User::where(...)->update([...]))不触发任何模型事件,因为绕过了单实例生命周期
查出数据后想改字段再保存,为什么 retrieved 后改了也不入库
'retrieved' 事件只负责通知“数据已从数据库取出”,它发生在模型实例化之后、属性赋值完成之时,此时修改属性不会自动标记为“已变更”,后续 save() 会跳过更新。
- 手动调用
$model->setDirty('field', $value)才能让字段进入变更检测队列 - 或者用
$model->forceFill(['field' => $value])->save()强制填充并保存(注意会忽略$fillable白名单) - 更安全的做法是在
'retrieved'监听器里只做读取后处理(如格式转换、关联预加载),不要依赖它来修改待持久化的状态 - 如果只是想统一处理返回值(比如把时间戳转为日期字符串),更适合放在模型的访问器(
getCreatedAtTextAttr)里,而不是事件里改原始属性
事件里抛异常会导致事务回滚失败吗
会,但仅限于当前模型操作所在的事务上下文 —— ThinkPHP 的事务由 Db::transaction() 或模型自身的 startTrans() 控制,事件回调本身不开启新事务。
- 在
'creating'或'updating'中 throw 异常,会中断保存流程,若外层有事务,则整个事务会被回滚 - 但在
'created'或'updated'中抛异常,数据已经落库,此时回滚只能靠手动Db::rollback(),框架不会自动补救 - 监听器内发起 HTTP 请求、写文件、发邮件等副作用操作,建议全部包裹在 try/catch 中,避免因外部失败污染主业务流程
- TP6.1+ 支持在事件监听器中使用
db()->transaction()开启嵌套事务,但要注意 MySQL 对 savepoint 的支持程度和框架封装是否透出底层能力
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











