新增失败时应先检查 save() 返回值及 geterrors() 错误信息,确认是否验证失败或数据库约束拦截;若 geterrors() 为空则排查数据库连接、权限、字符集及缓存问题。

新增失败时先看 save() 返回值和错误信息
Yii 的 save() 默认静默失败,不抛异常也不提示具体原因。直接用 if (!$model->save()) { var_dump($model->getErrors()); } 才能看到真实报错,比如字段超长、NOT NULL 约束触发、唯一索引冲突等。
常见现象:表单提交后页面刷新但数据没进库,控制台也没报错——本质是验证失败或数据库约束拦截,save() 返回 false 被忽略了。
-
getErrors()返回的是二维数组,格式为['username' => ['用户名已存在']],注意遍历打印 - 若返回空数组但依然没保存,说明不是验证层问题,要查数据库连接或权限(见下一条)
- 开启事务时,
save()失败不会自动回滚,需手动调用$transaction->rollBack()
Access denied for user ''@'localhost' 是配置漏了账号密码
这个报错不是模型逻辑问题,而是 config/db.php 或 main-local.php 里 username 和 password 写成了空字符串或注释掉了。Yii 2.0.49 会把空字符串当实际凭证传给 PDO,MySQL 就按匿名用户处理,然后拒绝。
检查点:
- 确认
config/main-local.php中'db' => ['class' => 'yii\db\Connection', 'username' => 'root', 'password' => '123456', ...]四个键都存在且非空 - 如果用的是 Docker 或远程 MySQL,
host不能写localhost(会走 socket),得写127.0.0.1或容器名,否则连都连不上,自然 save 失败 - MySQL 8.0+ 默认认证插件是
caching_sha2_password,PHP 7.4+ 支持,但旧版可能报 “Plugin is not loaded”,此时要么升级 PHP,要么重置用户认证方式:ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
新增字段后 save() 不生效,大概率是 $safeOnly = true 拦住了
Yii 默认只允许通过验证规则标记为 safe 的属性被批量赋值(mass assignment)。你加了新字段,但没在 rules() 里声明它为 safe,save() 就会跳过该字段——看起来“没保存”,其实是压根没读进来。
解决方法只有两个:
- 在模型的
rules()方法里加上['new_field', 'safe'],或者更明确地写['new_field', 'string']等具体验证规则 - 临时绕过安全检查:用
$model->load($post, '')(第二个参数为空字符串)或直接$model->new_field = $value单独赋值再save() - 别改
$safeOnly = false,这是危险操作,会打开所有字段的批量赋值入口,容易被参数注入利用
用 createCommand()->insert() 绕过模型层时要注意 SQL 注入和字符集
当确定是模型层逻辑太重、不想走验证时,可直接用 DAO 层插入:Yii::$app->db->createCommand()->insert('user', ['name' => $name, 'age' => $age])->execute();。但它不经过模型验证和事件,也跳过了 beforeSave 钩子。
关键细节:
- 所有变量必须通过参数绑定传入,不要拼字符串,尤其
$name含单引号时,拼接会导致 SQL 报错或注入 - 如果数据库用的是
utf8mb4(存 emoji 必须),而连接未设 charset,中文可能变???。确保config/db.php中有'charset' => 'utf8mb4' -
insert()不返回主键,如需获取刚插入的 ID,得用Yii::$app->db->getLastInsertID(),且必须在同一线程/连接中立即调用
最常被忽略的是:新增字段后没清 Yii 缓存,导致 getSafeAttributeNames() 还缓存着旧字段列表。执行 php yii cache/flush-all 再试一次,比反复改代码更快。











