yii2模型事件无固定列表,取决于方法调用、事务及批量操作;beforevalidate/aftervalidate仅在validate()或save()中触发;beforesave/aftersave在save()中执行,受事务影响;updateall()等批量操作不触发事件;deleteall()不触发beforedelete;自定义事件需实例化后注册。

Yii2 模型事件没有固定“全部列表”,实际可用的事件取决于你调用哪个方法、是否开启事务、是否批量操作——硬背事件名没用,得看触发上下文。
哪些方法会触发 beforeValidate 和 afterValidate
这两个事件只在显式调用 validate() 时触发(包括 save() 内部调用),不会在 load() 或赋值时自动触发。
- 常见错误:以为给属性赋值后事件就触发了,结果发现
beforeValidate根本没进断点 - 正确场景:需要在验证前动态加规则、清理字段、或根据其他字段状态禁用某验证项
-
validate()返回false时,afterValidate仍会触发,但errors已填充 - 注意:如果
scenarios里没包含当前属性,它可能被跳过验证,事件里也拿不到该字段的错误
beforeSave / afterSave 的执行时机和事务边界
它们在 save() 过程中触发,但关键点在于:是否在事务内、是否插入还是更新、是否跳过验证。
-
beforeSave在 SQL 执行前,可修改属性值(如设置created_at),返回false可中断保存 -
afterSave在 INSERT/UPDATE 成功提交后触发;但如果save()在事务中且后续回滚,afterSave已执行的逻辑(比如发消息、写日志)不会自动撤销 - 批量操作如
updateAll()、insertAll()不触发任何模型事件——这是最常被忽略的坑 -
insert()和update()方法也会触发对应事件,但它们绕过验证,beforeValidate等不会出现
为什么 beforeDelete 有时不触发?
只有通过模型实例调用 delete() 才触发;直接用 deleteAll() 静态方法不会触发任何事件。
- 典型误用:在控制器里写
User::deleteAll(['id' => $id]),结果beforeDelete里的清理逻辑(如删关联文件、清缓存)全失效 - 安全建议:敏感删除操作尽量走实例方法,哪怕先
findOne()再delete(),确保事件链完整 -
beforeDelete返回false可阻止删除,但注意:如果已启事务,它不会自动 rollback,需手动处理 - 软删除(如
status = 0)通常不用beforeDelete,更适合放在beforeSave里判断逻辑
自定义事件怎么注册才真正生效?
必须在模型实例化之后、操作之前注册,且不能依赖构造函数里写死的绑定——因为事件监听器是运行时行为,不是类定义的一部分。
- 错误做法:在
__construct()里直接$this->on(self::EVENT_BEFORE_SAVE, ...),看似可行,但若模型被find()->one()复用(比如 ActiveQuery 缓存了实例),监听器可能重复绑定 - 推荐做法:统一在控制器或服务层调用前绑定,例如
$model->on(Model::EVENT_AFTER_INSERT, [$service, 'logInsert']) - 监听器里抛异常不会自动回滚事务,除非你手动 throw
Exception并由外层捕获处理 - 用
off()解绑时要注意:匿名函数无法解绑,必须用命名函数或对象+方法数组
事件链不是线性流水线,而是分散在 ActiveRecord 生命周期各环节;真正难的不是知道有哪些事件,而是判断某个业务逻辑该塞进哪个事件、以及它会不会被批量操作绕过。











