gii生成的模型save()默认不触发自定义更新逻辑,需手动实现beforesave/aftersave;updateall()绕过生命周期,不会调用aftersave;rules()需补充业务规则,调试应使用日志而非var_dump。

生成的模型里 save() 默认不触发自定义更新逻辑
Gii 生成的模型类本身不带任何业务钩子,save() 调用的是 ActiveRecord 原生流程:校验 → 插入或更新 SQL → 触发 beforeSave/afterSave。但这些方法默认是空的,你得手动补全。
常见误区是以为改了数据库字段就能自动同步,比如改了 status 就该发消息、改缓存——其实什么都不会发生,除非你在 afterSave() 里写。
-
afterSave($insert, $changedAttributes)是唯一可靠入口:$insert 为 true 表示新增;$changedAttributes 是「本次实际变更的字段名 → 原值」数组,不是所有属性 - 别在
beforeSave里做耗时操作(如远程调用),它阻塞事务提交 - 如果用了
updateAll()或原生 SQL 更新,afterSave完全不会触发——这是设计使然,不是 bug
updateAll() 和 save() 的行为差异必须分清
这两者走的是完全不同的代码路径:updateAll() 绕过模型生命周期,直接拼 SQL 执行;save() 才会走 beforeSave → DB → afterSave 流程。
所以如果你在 afterSave 里写了同步缓存逻辑,但线上用了 User::updateAll(['status' => 1], ['id' => 123]),那缓存就永远不同步。
- 批量更新场景(如后台审核一批订单):要么改用循环
$model->save(),要么在updateAll()后手动补同步逻辑 - 想统一处理?可以封装一个
safeUpdate()方法,在里面调save()并捕获错误,但无法避免性能损耗 - 注意
$changedAttributes在updateAll()下为空数组,别指望它能告诉你改了啥
修改 Gii 生成的模型后,别漏掉关联字段的校验逻辑
Gii 根据表结构生成 rules(),但外键字段(如 author_id)、状态机字段(如 status 只能从 0→1→2)这类业务规则,它不会猜,也不会加。
结果就是:你改完 afterSave,却发现数据根本存不进去——因为 status 字段被 rules() 拦在门外,报 "Status must be either 0, 1 or 2" 这种错。
- 检查
rules()返回数组,确认状态字段、外键字段、时间戳字段是否都有对应 rule - 外键字段建议加
exist验证:['author_id', 'exist', 'targetClass' => User::class] - 时间字段(如
updated_at)若由 DB 自动更新,模型里就别设safe,否则可能被覆盖
调试 afterSave 时最容易忽略的点
你以为加了 var_dump('hit'); 就能看到输出?不一定。因为 afterSave 可能在 console 命令、API 请求、队列任务里被调用,输出位置各不相同。
- console 命令中:输出到终端,但若用了
nohup或守护进程,得重定向日志 - Web 请求中:若页面已返回,
var_dump会被丢弃;用\Yii::info()写日志更稳妥 - 事务回滚时:哪怕进了
afterSave,只要上层事务 rollback,你的同步操作(如发 MQ)可能已执行,造成不一致 - 别在
afterSave里调$this->refresh(),它会重新查库,而当前事务还没提交,看到的可能是旧值











