最常见原因是模型字段未在rules()中声明为safe或验证规则,导致批量赋值时被过滤;其次beforesave()未显式return、cli下schema缓存未刷新、或数据未进入dirty attributes也会引发该现象。

为什么 save() 返回 true 却没更新数据库?
最常见原因是模型属性未被识别为“可安全赋值”,尤其在批量赋值($model->attributes = $data)或 CLI 场景下。Yii2 默认只允许 rules() 中标记为 safe 的字段参与块赋值,哪怕字段已存在于表结构中,模型类里没声明,它就进不了 $model->attributes 内部数组,后续 save() 实际操作的是空值或旧值。
- 检查模型类的
rules()方法是否包含新字段,例如:['new_field', 'safe']或更严格的验证规则(如['new_field', 'string']) - 若字段纯属业务逻辑用、无需校验,加
['new_field', 'safe']是最轻量解法 - 不要依赖
save(false)绕过验证来解决字段不生效问题——它跳过校验,但不解决字段未加载的问题
beforeSave() 中忘记 return 会导致静默失败
如果模型重写了 beforeSave(),但函数体末尾没显式返回布尔值(比如只写了 parent::beforeSave($insert); 而没加 return),PHP 默认返回 null,Yii2 会将其视为 false 并中断保存流程。此时 $model->save() 仍返回 true(因方法执行完成),但数据库无变更,$model->errors 也为空。
- 务必确认
beforeSave()函数以return parent::beforeSave($insert);结尾 - 哪怕你只在里面加了日志或简单逻辑,也必须保留
return语句 - 可用
var_dump($this->getDirtyAttributes());在save()前验证哪些字段真被标记为“已修改”
CLI 环境下 Schema 缓存未刷新,导致新增字段“不存在”
在定时任务或命令行脚本中执行更新时,如果数据库已加字段、模型类也改了,但 save() 仍不写入新字段,大概率是 ActiveRecord 的 Schema 缓存没更新。Yii2 会缓存表结构元信息(比如字段列表、主键、类型),CLI 和 Web 请求使用的是同一套缓存组件,但开发中常只刷 Web 端缓存,忽略 CLI 场景。
- 临时验证:在脚本开头加
Yii::$app->cache->flush();(仅限调试,勿留生产) - 长期方案:执行迁移后立即运行
php yii cache/flush-schema - 若用 Redis,可手动执行
redis-cli -n {db} FLUSHDB,但注意是否与其他服务共用库
数据来源不是 load(),而是直接赋值时要注意字段白名单
当用 $model->field = $value 逐个赋值,通常不会出问题;但若用 $model->attributes = $array,Yii2 会严格按 scenarios() 和 rules() 过滤字段。即使字段物理存在,只要不在当前场景的白名单里,就会被丢弃。
- 查看当前场景:
var_dump($model->scenario);,默认是'default' - 确保
scenarios()方法中,'default'场景包含该字段,或显式设置$model->scenario = 'default'; - API 场景下若用 JSON body,需先
$data = \yii\helpers\Json::decode($request->getRawBody());,再$model->load($data, '')(第二个参数为空字符串绕过 formName 匹配)
$model->getDirtyAttributes() 返回什么——它暴露了 Yii2 真正打算写入数据库的字段和值。如果新字段根本不在里面,问题一定出在赋值阶段或 Schema 缓存上,而不是 SQL 执行本身。











