thinkphp模型观察者没触发,根本原因是未显式注册:tp6.1+需在模型中设protected $observe = yourobserver::class或在initialize()调用static::observe(),tp6.0必须手动注册;观察者类须继承think\model\observer,方法名严格为created、updated等过去式命名。

ThinkPHP 模型观察者为什么没触发?
模型观察者(Observer)在 ThinkPHP 6+ 中默认不自动注册,即使你写了 observe 方法并绑定了类,也不会生效——根本原因是没在模型里显式调用 observe() 或未在服务提供者中全局注册。
常见现象:增删改操作完全没日志、creating/updating 回调函数压根不执行。
- 确认模型类中是否调用了
protected $observe = YourObserver::class;(TP6.1+ 支持) - 若用的是 TP6.0,必须手动在模型的
initialize()里写static::observe(YourObserver::class); - 观察者类需继承
think\model\Observer,且方法名严格为created、updated、deleted(注意是过去式,不是create) - 事务内操作可能被回滚,但观察者已执行——日志写入不可逆,需在观察者内部判断
Db::transactionLevel() > 0并延迟记录或加标记
如何在观察者里拿到操作人和原始数据?
ThinkPHP 观察者回调接收的参数是模型实例,但默认不包含请求上下文(如登录用户 ID),也不保留修改前字段值——这两点必须自己补全。
- 操作人:推荐从容器取,比如
app('request')->header('x-user-id')(若接口统一透传),或通过think\Auth类(需提前注入);避免在观察者里直接调用session(),因 CLI 或队列场景 session 不可用 - 原始数据:在
saving或updating阶段,用$model->getOriginal()获取变更前值;但注意该方法只对已查询出的模型有效(即先find()再save()),批量update()静态调用时拿不到原始值,此时应改用事件监听model_after_update+ 查询原记录 - 敏感字段过滤:记录日志前建议用白名单剔除密码、token 等字段,例如
array_diff_key($data, array_flip(['password', 'api_token']))
日志表设计与批量写入性能问题
直接在观察者里每条操作都 LogModel::create(),高并发下容易拖慢主业务,尤其日志表无合理索引时会锁表。
- 日志表至少要有:
user_id(操作人)、model(如'app\model\User')、record_id(变动记录主键)、action('create'/'update')、before_data和after_data(JSON 字段,MySQL 5.7+ 推荐用JSON类型而非TEXT) - 避免在观察者里做耗时操作:把日志数据推到
think-queue或 Redis List,由消费者异步落库;若不用队列,至少用Db::execute()批量插入,而不是循环create() - 注意
before_data和after_data字段长度限制:MySQLJSON类型最大 1GB,但实际建议单条日志控制在 100KB 内,超长字段可截断并加标记"truncated": true
软删除和关联模型的变动怎么记?
delete() 方法调用后,观察者触发的是 deleting 和 deleted,但软删除(softDelete)实际只是更新 delete_time 字段,不会进 deleted 钩子——它走的是 updating 流程。
- 区分物理删除和软删除:在
updating回调里检查$model->getOrigin('delete_time') === null && $model->delete_time !== null,视为软删除动作 - 关联模型变动不会自动触发父模型观察者:比如订单模型更新了,其关联的订单商品(
OrderItem)变动需单独定义OrderItemObserver,并在对应模型中绑定 - 一对多关系中「级联删除」的日志记录要小心:主模型
delete()后,关联模型的deleted钩子按顺序触发,但事务回滚时它们已写入日志——无法自动撤回,只能靠日志表里的is_rollback字段事后标记
最易被忽略的一点:观察者只监听当前模型的生命周期事件,不感知数据库触发器、原生 SQL 或 Db::table()->update() 等绕过模型的操作。这类变动必须另起路径捕获,比如在中间件拦截关键路由,或用数据库 binlog 解析。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











