Yii事件需手动trigger()触发,三步缺一不可:定义事件名常量、在Component子类实例上调用trigger()、传入yii\base\Event子类实例;绑定与触发须同对象实例,全局监听需配对off()防泄漏。

Yii 的事件不会自动触发,必须手动调用 trigger();不调用它,哪怕绑了十次监听器也没用。
怎么正确触发一个自定义事件
触发事件不是“写个方法名就自动响”,而是三步缺一不可:定义事件名常量、在目标对象上调用 trigger()、传入合法的事件对象。
-
trigger()必须在继承自yii\base\Component(或其子类如ActiveRecord)的对象实例上调用,普通 PHP 类不行 - 事件名要和绑定时完全一致——大小写敏感,且推荐用常量(如
const EVENT_AFTER_PAY = 'afterPay';),避免字符串散落各处 - 第二个参数必须是
yii\base\Event或其子类实例;如果只传数组或字符串,$event->sender和$event->data会丢失或异常 - 示例:
$order->trigger(Order::EVENT_AFTER_PAY, new OrderEvent(['order' => $order]));
为什么 on() 绑定了却没执行
最常见的是“名字对不上”或“作用域错位”。Yii 不做魔法匹配,只认你传进去的字符串或常量。
- 检查绑定和触发是否在同一个对象实例上:用
$model->on()绑的,只能被$model->trigger()触发;换成另一个$model2就无效 - 全局绑定要用
Event::on(Order::class, Order::EVENT_AFTER_PAY, ...),第一个参数必须是类名字符串,不是对象 - IDE 跳转失效?大概率是没声明常量,改用字符串硬编码(如
'afterPay')导致重构风险高,也难 debug - 加一句
\Yii::debug('about to trigger ' . Order::EVENT_AFTER_PAY);在trigger()前,确认代码真跑到了那里
用 Event::on() 全局监听要注意内存泄漏
全局监听适合跨模块响应(比如支付成功后发短信、更新库存),但它绕过对象生命周期管理,容易把 handler 锁在内存里。
- 每次调用
Event::on()都会追加 handler,测试中反复 reload 页面可能注册 N 次同一逻辑 - 必须配对使用
Event::off()解绑,尤其在单元测试 tearDown() 或长任务脚本结束前 - 多个模块都监听同一事件时,执行顺序按注册先后,不保证稳定性;需要强顺序控制,应改用消息队列或显式调用链
- 别试图在
Event::on()里监听beforeAction这类框架级事件来“拦截请求”——Yii 没有 before/after 拦截链,只有单次广播
真正容易被忽略的是事件对象的继承——哪怕只传一个 ID,也要让自定义事件类继承 yii\base\Event,否则 $event->sender 为空、$event->data 无法自动注入、调试时连是谁触发的都看不到。


















