最常见原因是新字段未在rules()中声明为safe或对应验证规则,导致save()静默过滤该字段;即使attributes批量赋值或save(false)也无法绕过此安全机制。

为什么 $model->save() 返回 true 却没更新字段
最常见的情况是:新字段没加进模型的 rules(),或没标为 safe。Yii2 的 save() 默认只保存 safe 属性,哪怕你用 $model->attributes = $data 批量赋值,没进白名单的字段也会被静默过滤。
实操建议:
- 检查模型类中
rules()是否包含新字段,例如:['new_field', 'safe']或更具体的验证规则(如'string') - 确认赋值方式:若用
$model->load($post),必须确保字段名在scenarios()对应场景里被标记为safe - 不要依赖
save(false)绕过验证就以为能绕过属性过滤——它只跳过验证逻辑,不恢复非safe字段的写入权限 - 临时加一行调试:
var_dump(array_keys($model->getDirtyAttributes()));,看实际准备更新的字段列表里有没有你的新字段
表单提交后 utime 更新了但业务字段没变
这说明数据已进入模型、通过验证、也触发了保存流程,但字段被 Yii 的属性过滤机制拦截了。典型诱因是字段名大小写不一致、下划线命名与驼峰映射错位,或数据库字段类型和 PHP 类型不兼容(比如 DB 是 TINYINT(1),PHP 传了字符串 "1" 而模型规则要求 integer)。
实操建议:
- 用
$model->getAttributes()和$model->getOldAttributes()对比,确认新值是否真进了模型实例 - 检查数据库字段是否允许 NULL —— 若字段设为
NOT NULL且无默认值,而 PHP 传的是空字符串或null,MySQL 可能静默转成0或报 warning,但 Yii 不抛异常 - 运行
$model->save()后立刻查$model->errors,别只看返回值;有时验证通过但 DB 层写入失败(如唯一索引冲突),save()仍可能返回true(取决于事务配置)
CLI 环境下更新失败但 Web 正常
CLI 模式不会自动加载 session 或 cookie,而某些自定义验证器、行为(Behavior)或扩展依赖会话状态(比如基于 session 的权限校验、缓存键生成)。此时 save() 可能中途静默退出,或触发未捕获的异常导致事务回滚。
实操建议:
- 在 CLI 脚本开头强制设置环境:
defined('YII_ENV') or define('YII_ENV', 'console');,避免被误判为 web 环境 - 禁用可能依赖 session 的组件:在 console 配置里把
user、session组件设为 null,或换用无状态替代方案 - 包裹保存逻辑进 try/catch,并打印完整异常:
catch (\Exception $e) { echo $e->getMessage() . "\n" . $e->getTraceAsString(); } - 确认数据库连接在 CLI 下走的是同一套配置——有时
console.php和web.php的db组件参数不一致(如 charset、tablePrefix)
批量更新时部分字段始终被忽略
当用 ActiveRecord::updateAll() 或 updateCounters() 时,字段名必须严格匹配数据库列名(区分大小写),且不能是表达式或函数调用(除非显式用 Expression 包裹)。另外,updateAll() 不触发模型事件和验证,所以别指望它执行 beforeSave 里的逻辑。
实操建议:
- 避免直接传数组给
updateAll(),改用键值对明确指定:Article::updateAll(['status' => 1, 'utime' => new \yii\db\Expression('NOW()')], ['id' => $id]) - 如果字段名含保留字(如
order、group),必须用反引号包裹:`order` - 调试时先用
createCommand()->update()手写 SQL,确认语句能执行成功,再退回到 AR 层排查映射问题 - 注意 MySQL 的
STRICT_TRANS_TABLES模式:若传了超长字符串进VARCHAR(10),会直接报错而非截断,而 Yii 默认不暴露该错误
errors 数组,得从数据进模型、到进 SQL、再到落库的每一步,用 var_dump 或日志戳出真实值。


















