ThinkPHP 中最接近“数据开始”的是模型的 beforeWrite 事件和 Db 的 before_select 等显式注册事件;beforeWrite 在写入前统一触发,适合通用预处理;Db 事件需在 AppService@register 中全局注册,不可在控制器或事务中动态绑定。

ThinkPHP 的“事件”不是指数据库操作开始的钩子,也没有叫 data_start 或 begin 这类内置事件方法。你真正想用的,是模型生命周期事件(如 beforeInsert)或数据库查询事件(如 before_select),它们在数据操作「实际执行前」触发,这才是最接近“数据开始”的语义。
模型事件里没有 beforeStart,但有 beforeWrite
模型类中不存在 beforeStart 方法,官方支持的写入前置事件只有 beforeWrite——它覆盖插入和更新两种场景,比单独写 beforeInsert 或 beforeUpdate 更早、更统一。
-
beforeWrite在数据写入数据库前调用,此时模型属性已赋值、验证已完成,但 SQL 还没发出去 - 它不区分 INSERT/UPDATE,适合做通用预处理:比如统一设置
updated_at、生成关联 ID、校验业务规则 - 注意:如果模型启用了自动时间戳(
protected $autoWriteTimestamp = true),beforeWrite里再手动设create_time可能被覆盖,得关掉自动写入或改用beforeInsert
Db 类的 before_select 等事件必须显式注册
Db 查询事件(如 before_select、before_update)不会自动生效,必须在服务启动时用 Db::event() 显式绑定,否则函数根本不会被调用。
- 推荐注册位置:在
app/provider/AppService.php的register()方法里 - 示例:
Db::event('before_select', function($query) { $query->where('status', '>=', 0); }); - 别在控制器里注册——每次请求都重绑一次,既浪费又可能重复触发
- 同一个事件可注册多个回调,按注册顺序执行;但不要依赖执行顺序做关键逻辑,容易出错
事务开启不是事件,而是独立流程控制
有人把 Db::startTrans() 当作“数据开始事件”,但它只是 PDO 事务控制指令,不带任何钩子能力。它不触发事件,也不接受回调。
立即学习“PHP免费学习笔记(深入)”;
- 事务内所有 Db 操作共享同一个连接,但
before_insert等模型事件仍会照常触发 - 事务失败后回滚,不会“撤销”已触发的
afterInsert日志记录——这点极易被忽略,日志写入要放在commit成功之后,或用事务型日志表 - 别在事务块里调用
Db::event()注册新事件——事务结束不影响事件注册,但注册时机错乱会导致调试困难
真正难处理的是事件与事务的耦合点:比如 afterUpdate 里发消息,但事务最终 rollback 了,消息却已发出。这种不一致没法靠框架自动解决,得靠补偿逻辑或延迟投递。



















