thinkphp模型事件仅在调用模型方法(如save、delete)时触发,db类操作不触发;必须在protected static init()中注册,支持多回调顺序执行,before_*返回false可中断操作;after_update仅在更新时触发且$id必有值,after_write适用于新增和更新但需isupdate()判断;事件回调仅传$model,无请求参数,批量操作不支持单条上下文。

模型事件在 ThinkPHP 中不是“开了就能用”的功能,它只在明确走模型方法(save、delete、insert 等)时触发;用 Db 类直接操作数据库,或调用 isUpdate() 判断状态,事件完全不会执行。
模型事件必须注册在 init() 静态方法里
ThinkPHP 不支持在构造函数或普通方法中注册事件。所有事件绑定必须写在模型类的 init() 方法中,且该方法需为 protected static 修饰。
-
self::event('before_insert', function ($model) { ... })是通用写法,兼容所有版本 -
self::beforeInsert(function ($model) { ... })是 5.0.4+ 提供的快捷方式,更直观但命名需严格匹配(如不能写成before_insert) - 多个同类型事件可重复调用
event()注册,它们会按注册顺序依次执行 - 若回调返回
false(仅限before_*类事件),当前操作将中断,后续模型逻辑(如 SQL 执行)不会发生
after_update 和 after_write 的关键区别
after_update 只在显式调用 save() 且模型已存在主键值时触发;而 after_write 在任何写入动作(新增或更新)后都触发,更适合做统一日志记录。
- 使用
after_update时,$model->id一定有值,适合做关联更新(如同步缓存) - 使用
after_write时,需手动判断是新增还是更新:$model->isUpdate() ? 'update' : 'insert' - 如果在
after_write中执行耗时操作(如发消息、写文件),建议异步处理,否则会拖慢主请求响应 -
after_insert回调里的$model->id是刚插入生成的主键,但此时事务尚未提交,高并发下慎用于强一致性场景
事件回调里拿不到原始请求参数,得自己传
模型事件回调函数只默认传入模型实例($model),$_GET、$_POST、Request 对象等都不自动注入。常见错误是直接在回调里调用 input() 或 param(),结果取到空值或上一个请求的数据。
- 正确做法是在控制器中先拿到参数,再通过模型属性临时挂载:
$user->operator_id = $request->param('operator_id'); - 或者在模型基类中重写
save(),把必要上下文塞进模型的data或自定义属性,再在事件中读取 - 依赖注入(如
function ($model, Request $request))仅在部分版本 + 特定配置下生效,不推荐作为主要方案 - 调试时可用
dump($model->getData())查看当前写入的字段,但注意它不包含未赋值的默认字段
真正容易被忽略的是:模型事件无法捕获批量操作(如 User::destroy([1,2,3]))中的单条记录上下文——before_delete 回调收到的是整个数据集数组,不是单个模型实例。这种场景必须改用循环单条 delete(),或改用数据库事件(Db::event('before_delete', ...))配合查询构造器处理。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











