ThinkPHP 6.0 模型事件必须通过 Event 类或 observe() 方法注册,不再支持 $event 数组和魔术方法;事件参数变为 [$data, $modelClass];5.1/5.2 的 before_write 触发时机不同,需注意验证顺序;跨版本迁移需清理隐式方法并检查事务一致性。

ThinkPHP 6.0 的模型事件注册方式变了
6.0 彻底移除了 protected $event 数组写法,也不再支持在模型类里直接定义 onBeforeInsert 这类魔术方法。事件必须通过 Event 类或模型的 observe() 方法绑定——否则监听完全不触发。
常见错误是把 5.x 的写法直接搬过来,比如:
protected $event = [
'before_insert' => 'beforeInsert'
];
这在 6.0 中静默失效,连 warning 都没有。正确做法是统一用观察者模式:
- 新建一个观察者类,实现
think\contract\EventObserverInterface,并在对应方法里写逻辑 - 在模型中调用
static::observe(YourObserver::class),建议放在initialize()里 - 如果要用闭包临时监听,可用
Event::listen('model.before_write', function($model){...}),但注意生命周期仅限当前请求
ThinkPHP 5.1 和 5.2 的 before_write / after_read 触发时机不一致
5.1 中 before_write 在数据验证之后、SQL 构建之前触发;而 5.2 开始提前到验证前,导致你在里面改字段可能被验证规则拦截,报 ValidateException。
立即学习“PHP免费学习笔记(深入)”;
典型踩坑场景:想在 before_write 里自动补 updated_at,但模型同时设置了 validate 规则要求该字段非空——5.1 没事,5.2 就会失败。
- 5.1:验证 →
before_write→ 写入 - 5.2+:
before_write→ 验证 → 写入 - 若需兼容,把字段赋值逻辑移到
autoWriteTimestamp或手动在save()前处理,别依赖事件顺序
6.0+ 的事件参数从对象实例变成数组 + 模型类名
5.x 事件回调接收的是模型实例($model),而 6.0+ 统一为 [$data, $modelClass] 形式,其中 $data 是待写入的原始数组,$modelClass 是完整类名字符串(如 'app\model\User')。
这意味着你不能再直接调用 $model->id 或 $model->save() ——它根本不是对象。重写时容易漏掉这层转换:
- 需要访问模型能力?先用
new $modelClass()实例化,或用Db::name()手动查 - 想修改写入数据?直接改
$data数组,它是传引用的 - 日志或调试时打印
$modelClass比打印$data更能定位来源
跨版本迁移时最容易忽略的隐式事件覆盖
5.x 支持在模型里定义 beforeInsert、afterUpdate 等方法,框架会自动识别并调用;6.0+ 不再扫描这些方法名,但如果你没删干净,它们会变成普通方法,看似“还在运行”,实则已脱离事件生命周期——比如事务回滚时不会回退这些方法里的操作。
重写时务必做两件事:
- 全局搜索项目中所有
before*/after*方法,确认是否被observe()显式绑定 - 检查事务边界:6.0 的事件默认不在事务内,如需强一致性,得手动用
Db::transaction()包裹或监听db.after_execute
模型事件不是“写完就跑”,它的执行阶段、参数结构、事务包裹粒度,每个版本都悄悄挪了位置。重写时盯着文档里的「触发时机」小字比看示例代码更重要。



















