yii 3.0 中事件与行为必须通过 di 容器在构造时显式注入,禁止动态挂载和 owner 访问;行为类需实现 behaviorinterface 并依赖构造注入;事件监听须绑定具体类实例,且状态变更必须经统一状态机入口触发。

要在 Yii 3.0 中正确配置事件与行为,必须放弃 Yii 2.x 的全局事件注册和 $model->attachBehavior() 模式,因为 ServiceLocator 和隐式 owner 绑定已彻底移除;所有事件监听与行为挂载都需通过依赖注入容器显式声明、在构造时完成绑定,否则行为无法访问上下文,事件监听器将收不到任何触发。
定义可复用的行为类
创建一个独立 PHP 类,实现 Yiisoft\Behavior\BehaviorInterface,不要继承任何基类;该类必须声明构造参数以接收运行时依赖,例如 private CacheInterface $cache 或 private LoggerInterface $logger。
在类中实现 beforeSave()、afterFind() 等钩子方法——这些方法不再自动调用,必须由你手动在模型生命周期中触发,或通过事件监听间接调用。
行为类内部禁止使用 $this->owner 访问模型实例;所有数据交互必须通过构造注入的依赖或显式传入的上下文对象完成。
在模型中声明并使用行为
Yii 3.0 不再支持 $model->attachBehavior() 动态挂载;行为必须在模型构造时通过 DI 容器注入,并在属性中保存引用。
在模型类中添加私有属性: private BehaviorInterface $auditBehavior;
在构造函数中接收该行为实例:public function __construct(private AuditBehavior $auditBehavior) { ... };这确保行为与模型生命周期严格对齐,避免因延迟挂载导致状态不一致。
【行为不可在模型 save() 内部 new 实例】 否则会绕过 DI 容器,丢失日志、缓存等注入依赖,且无法被测试替换成 mock 实例。
事件监听器的注册方式
方法一:在 DI 配置中绑定事件监听器
在 config/common.php 中添加如下条目:EventDispatcherInterface::class => static function (ContainerInterface $container) { $dispatcher = new EventDispatcher(); $dispatcher->addListener(Order::EVENT_STATUS_CHANGED, [$container->get(OrderStatusChangedListener::class), 'handle']); return $dispatcher; };
方法二:在控制器或服务类中手动触发监听
获取事件分发器实例:$dispatcher = $this->eventDispatcher;,然后调用 $dispatcher->dispatch(new OrderStatusChanged($order));;注意:事件对象必须是具体类实例,不能传字符串事件名。
方法三:使用 PSR-14 兼容的监听器类
监听器类需实现 __invoke(EventInterface $event) 方法,并在容器中注册为可调用对象;框架会自动识别并注入依赖,例如 private EmailService $email 可直接使用。
统一事件入口与状态机协同
第一步:创建事件上下文封装类,如 OrderTransitionContext,包含订单实例、操作人、来源渠道等元信息。
第二步:在 OrderStateMachine 的 apply() 方法末尾,显式调用 $this->eventDispatcher->dispatch(new OrderStatusChanged($context));。
第三步:确保所有状态变更路径(包括后台脚本、API 接口、定时任务)都必须经过 $stateMachine->apply() 入口,否则事件不会触发,日志、通知、库存扣减等副作用将丢失。
这一步不可跳过,【事件触发必须与状态流转强绑定,不能分散在各处手动 dispatch】,否则会出现部分环节漏发通知、审计日志断层等问题。











