如何使用ThinkPHP的Observer模式实现模型事件监听与解耦?
- 内容介绍
- 文章标签
- 相关推荐
本文共计908个文字,预计阅读时间需要4分钟。
ThinkPHP的Observer模式并非注册即生效,必须手动绑定模型与事件系统。具体操作如下:
为什么 User::observe(UserObserver::class) 没反应?
这是最常卡住的地方:静态调用 observe() 只影响后续通过该模型实例发起的操作,不作用于静态方法(如 create()、update())。
-
User::observe(UserObserver::class)后再调用User::create($data)—— 不会触发,因为create()是静态方法,不走实例生命周期 - 正确写法是:
(new User())->observe(UserObserver::class)->create($data),或改用实例方式操作 - 若坚持用静态方法,必须在应用启动时(如
app\common\event.php)调用think\facade\Event::observe(User::class, UserObserver::class) - 检查
config/model.php中'event' => true—— 这是模型事件总开关,设为false会直接禁用所有observe行为
created 和 saving 该选哪个做数据清洗?
取决于你要改的是“入库前”还是“入库后”的数据。
-
saving:在save()或create()执行前触发,$model还没写库,可安全修改字段,返回false能中断保存 -
created:主键已生成并写入数据库,此时改$model->name不会自动更新,除非你再调一次$model->save() - 常见误用:
retrieved里改属性,然后期望下次save()自动更新 —— 不会。该事件只通知“已取数”,不标记脏数据
批量操作(where()->update())为何监听不到事件?
因为批量更新绕过了单模型实例的生命周期,不创建 Model 对象,自然不触发任何模型事件。
立即学习“PHP免费学习笔记(深入)”;
-
User::where('id', 1)->update(['name' => 'x'])—— 零事件 - 想监听这类操作,只能退回到底层 SQL 监听(如重写
Query类),或改用循环 + 单实例save() - 软删除场景下,
delete()触发的是deleting/deleted,但真正物理删除需监听forceDeleted - 如果模型用了
use SoftDelete,deleted永远不会被调用,除非显式forceDelete()
Observer 类方法名写错、命名空间漏了怎么办?
ThinkPHP 不校验方法签名,写错就静默失效,连日志都不打。
- 方法名必须严格小驼峰:
created()、updated()、deleted()—— 写成onCreated()或Created()都无效 - 类必须完整命名空间路径,且类文件得在自动加载范围内;别写
UserObserver,要写app\observer\UserObserver - 观察者类无需实现特定接口,但若想更规范,可实现
think\contract\ObserverInterface - 多个模型共用逻辑?别复制粘贴 Observer,抽成
Trait,在各 Observer 中use即可
最容易被忽略的一点:TP6.3+ 引入了 modelEvent 配置项,它和 config/model.php 里的 event 不是一回事。后者关了,整个模型事件系统就停摆;前者关了,observe() 也照样不工作——两者都得开。
本文共计908个文字,预计阅读时间需要4分钟。
ThinkPHP的Observer模式并非注册即生效,必须手动绑定模型与事件系统。具体操作如下:
为什么 User::observe(UserObserver::class) 没反应?
这是最常卡住的地方:静态调用 observe() 只影响后续通过该模型实例发起的操作,不作用于静态方法(如 create()、update())。
-
User::observe(UserObserver::class)后再调用User::create($data)—— 不会触发,因为create()是静态方法,不走实例生命周期 - 正确写法是:
(new User())->observe(UserObserver::class)->create($data),或改用实例方式操作 - 若坚持用静态方法,必须在应用启动时(如
app\common\event.php)调用think\facade\Event::observe(User::class, UserObserver::class) - 检查
config/model.php中'event' => true—— 这是模型事件总开关,设为false会直接禁用所有observe行为
created 和 saving 该选哪个做数据清洗?
取决于你要改的是“入库前”还是“入库后”的数据。
-
saving:在save()或create()执行前触发,$model还没写库,可安全修改字段,返回false能中断保存 -
created:主键已生成并写入数据库,此时改$model->name不会自动更新,除非你再调一次$model->save() - 常见误用:
retrieved里改属性,然后期望下次save()自动更新 —— 不会。该事件只通知“已取数”,不标记脏数据
批量操作(where()->update())为何监听不到事件?
因为批量更新绕过了单模型实例的生命周期,不创建 Model 对象,自然不触发任何模型事件。
立即学习“PHP免费学习笔记(深入)”;
-
User::where('id', 1)->update(['name' => 'x'])—— 零事件 - 想监听这类操作,只能退回到底层 SQL 监听(如重写
Query类),或改用循环 + 单实例save() - 软删除场景下,
delete()触发的是deleting/deleted,但真正物理删除需监听forceDeleted - 如果模型用了
use SoftDelete,deleted永远不会被调用,除非显式forceDelete()
Observer 类方法名写错、命名空间漏了怎么办?
ThinkPHP 不校验方法签名,写错就静默失效,连日志都不打。
- 方法名必须严格小驼峰:
created()、updated()、deleted()—— 写成onCreated()或Created()都无效 - 类必须完整命名空间路径,且类文件得在自动加载范围内;别写
UserObserver,要写app\observer\UserObserver - 观察者类无需实现特定接口,但若想更规范,可实现
think\contract\ObserverInterface - 多个模型共用逻辑?别复制粘贴 Observer,抽成
Trait,在各 Observer 中use即可
最容易被忽略的一点:TP6.3+ 引入了 modelEvent 配置项,它和 config/model.php 里的 event 不是一回事。后者关了,整个模型事件系统就停摆;前者关了,observe() 也照样不工作——两者都得开。

