thinkphp模型事件是硬编码约定的,需在模型类中定义public无参的before_insert、after_insert等方法才能自动触发,不依赖监听器注册,且仅在save()等模型操作时生效。

模型事件不是靠“监听器注册”触发的,而是模型类里直接定义方法名就能自动响应——写错名字或没配好开关,事件就完全静默,连报错都没有。
before_insert 和 after_insert 方法必须定义在模型类里
ThinkPHP 的模型事件是硬编码约定的,不是通过事件系统注册。只要你在模型类中定义了 before_insert 或 after_insert 这样的方法,框架在对应生命周期阶段就会自动调用它。
- 方法必须是
public,且不能带参数(框架内部不传参,强行加参数会导致 Fatal Error) - 不能写成
onBeforeInsert或handleBeforeInsert——只有字面量匹配才生效 - 如果模型继承了基类(比如
BaseModel),事件方法可以放在基类里,子类会继承并触发 - 注意:这些方法只在使用
save()、create()或insert()等主动写入操作时触发;直接执行Db::table()->insert()不走模型,事件不会执行
事件不触发的三个高频原因
绝大多数“事件没反应”的问题,都卡在这几个地方:
-
$this->exists为true时调用save(),会走 update 流程,触发的是before_update/after_update,不是 insert 事件 - 模型配置了
protected $autoWriteTimestamp = false;或未声明$createTime字段,但你以为before_insert会补时间——其实它不会,那是自动填充干的事,和事件无关 - 用了静态方法如
UserModel::create($data),但事件方法写在实例上(比如用了$this->name),此时$this是静态上下文,可能为null或报错;应改用static::event('before_insert', function() {})这种静态事件注册(仅限 TP8.1+),或统一走实例化 +save()
before_update 里怎么安全读旧数据
before_update 方法执行时,模型对象已经从数据库查出,但新值尚未写入。你可以用 $this->getOrigin('field') 拿原始值,比直接读属性更可靠。
-
$this->name是当前赋的新值(可能还没验证) -
$this->getOrigin('name')是从数据库加载时的原始值(类型已转换,可直接比较) - 不要依赖
$this->data或$this->origin数组——它们是受保护属性,结构可能随版本变化;getOrigin()是稳定接口 - 如果字段被设为
readonly或做了类型转换(如'status' => 'integer'),getOrigin()返回的已是转换后类型,不用再(int)强转
after_delete 后无法查刚删的数据
after_delete 执行时,记录已在数据库中删除,再调用 $this->find() 或 $this->toArray() 都会返回空或抛异常——这不是 bug,是设计使然。
- 需要留痕的话,应在
before_delete里把关键字段存到临时变量或日志:$this->deletedAt = date('Y-m-d H:i:s'); $this->logDeletion(); - 别在
after_delete里调用$this->save()或任何写库操作——模型已标记为exists = false,TP8 会拒绝写入 - 如果要级联软删关联数据,得在
before_delete中手动查出关联 ID 并更新,而不是等after_delete再查
模型事件最易被忽略的一点:它不跨请求、不跨进程、不进队列。你在命令行执行一个 php think run 命令触发的 after_insert,和用户 HTTP 请求触发的,完全是两个独立上下文——别指望用它做跨服务通知或延迟任务。真要解耦,得显式发消息或投递任务。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











