验证失败无提示的根本原因是load()未执行、validate()未触发或视图未读取$model->errors;三者缺一即导致错误静默消失。

验证失败却看不到错误提示,基本是因为 load() 没成功、validate() 没触发、或视图里压根没读 $model->errors —— 这三步漏掉任意一个,错误就“静默消失”。
控制器里调用 validate() 前必须先 load()
常见现象:写了 $model->validate(),但 $model->hasErrors() 始终是 false,打印 $model->errors 为空数组。
-
validate()只校验模型当前已有的属性值,不会自动从请求中取数据;不调load(),模型属性还是初始空值,自然“全验证通过” - POST 表单默认按
$model->formName()匹配字段名,比如User模型会找user[name],手写表单没加前缀就匹配不上 - API 场景(
application/json)下,load()默认不读 raw body,得手动解析:$data = \yii\helpers\Json::decode(Yii::$app->request->getRawBody()),再$model->load($data, '')(第二个参数传空字符串绕过 formName)
视图里不显式输出 $model->errors 就等于没提示
ActiveForm 小部件能自动渲染错误,但前提是它绑定了正确模型、且表单字段名与模型属性一致。如果手写 HTML 表单或用了自定义渲染逻辑,很容易忽略这一步。
- 别只依赖
ActiveForm::begin(),检查是否漏了field()调用,比如$form->field($model, 'email')—— 没这行,错误就不会附着到对应输入框下 - 想手动显示全部错误:
if ($model->hasErrors()) { foreach ($model->getErrors() as $attribute => $errors) { echo "$attribute: " . implode(', ', $errors); } } - 只显示第一个错误更常用:
$model->getFirstError('email'),适合做 inline 提示或 tooltip
addError() 写对了,但场景(scenario)没激活也没用
规则里写了 ['email', 'email'],但验证时没报邮箱格式错?大概率是场景不匹配导致整条规则被跳过。
- 模型类必须明确定义
scenarios()方法,不能只靠rules();默认'default'场景不会自动包含所有属性 - 规则中若写了
'on' => ['login'],那它只在$model->scenario = 'login'时生效;没写on的才对所有场景起作用 - 控制器中必须显式赋值:
$model->scenario = 'signup',构造函数传参或配置文件设的scenario不会自动生效
数据库唯一约束冲突时,validate() 不会捕获,得靠 try/catch
比如手机号加了 MySQL UNIQUE 索引,用户重复提交时,validate() 仍返回 true(因为唯一性验证器没启用),直到 save() 才抛出 \yii\db\IntegrityException。
- 补救方案:在
rules()中显式加['phone', 'unique'],这样validate()就能提前报错 - 如果已上线且不想改规则,就在控制器里
try/catch:if (strpos($e->getMessage(), 'Duplicate entry') !== false) { $model->addError('phone', '该手机号已被注册'); } - 注意:这种异常不是模型验证错误,不会进
$model->errors,必须手动addError()才能在视图里显示
最易被忽略的是 load() 和 scenario 这两层——它们不出错、不抛异常、也不警告,只是让验证彻底失效。调试时优先确认这两点,比翻 rules 更快定位问题。











