addError()仅存错不显示,需主动传递模型并渲染;用ActiveForm可自动显示错误,手写表单须手动输出getErrors()且防XSS;错误不跨请求,redirect后丢失。

模型里调用 addError() 后,错误不会自动显示
这是最常见的误解。调用 $model->addError('name', '用户名不能为空') 只是把错误信息存进模型的 errors 数组,它不触发页面渲染、不弹窗、不写入 session,也不会自动出现在视图里。你必须主动读取并输出它。
常见错误现象:控制器里写了 addError(),但刷新页面什么都没看到;或者只在调试时 var_dump($model->errors) 看到数据,线上却没提示。
- 验证失败后,
$model->validate()返回false,但不会中断流程 —— 你得自己判断是否跳转或重渲染 -
addError()是模型层操作,和视图完全解耦;它不关心你是用ActiveForm还是手写 HTML 表单 - 如果模型没传给视图(比如忘记
return $this->render('xxx', ['model' => $model])),那视图根本拿不到错误
用 ActiveForm 就能自动显示错误
只要你在视图中用了 Yii2 的 ActiveForm 小部件,并且字段绑定的是同一个模型实例,错误会自动渲染在对应字段下方。
示例(视图中):
<?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'username')->textInput() ?> <?= $form->field($model, 'email')->textInput() ?> <?php ActiveForm::end(); ?>
前提是控制器里做了验证并保留了模型状态:
public function actionCreate()
{
$model = new User();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$model->save();
return $this->redirect(['index']);
}
// 注意:这里没 redirect,而是直接 render,保证 $model 带着 errors 进入视图
return $this->render('create', ['model' => $model]);
}
-
ActiveForm::field()内部会调用$model->getErrors('username'),所以字段名必须和addError()的第一个参数严格一致(区分大小写) - 若想自定义错误 HTML 结构,可覆盖
ActiveForm::error()或配置fieldConfig['errorOptions'] - 不要在
addError()后又调用$model->save()却忽略返回值 ——save()内部也会调用validate(),可能覆盖你手动加的错误
手写表单时,必须手动输出 $model->getErrors()
不用 ActiveForm?那就得自己遍历 $model->errors 并拼 HTML。它是个二维数组:['username' => ['用户名不能为空'], 'email' => ['邮箱格式错误']]。
简单安全的写法(视图中):
<?php if ($errors = $model->getErrors()): ?>
<div class="alert alert-danger">
<ul>
<?php foreach ($errors as $attribute => $msgs): ?>
<?php foreach ($msgs as $msg): ?>
<li><?= htmlspecialchars($msg) ?></li>
<?php endforeach; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
- 务必对
$msg做htmlspecialchars(),防止 XSS(尤其当错误消息含用户输入时) - 不要直接 echo
$model->errors—— 它是数组,会输出Array字符串 - 若只关心某个字段的错误,用
$model->getFirstError('username')获取首条,比遍历更轻量
别和 setFlash() 混用,它们解决不同问题
addError() 是模型级、字段级、即时验证错误;setFlash() 是 session 级、页面级、跳转后提示(比如“保存成功”)。两者目的、生命周期、使用时机完全不同。
典型误用场景:
- 在
save()失败后,既调addError()又调setFlash('error', '保存失败')—— 用户看到两套重复提示 - 验证失败后
redirect(),再用setFlash()显示错误 —— 这时模型字段级错误丢失,只能靠泛化提示,无法定位具体哪一栏错了 - 把
setFlash()当成addError()的替代品,结果表单提交后页面清空,错误没绑定到字段上
真正需要跨请求传递字段级错误的情况极少,基本意味着设计出了问题 —— 验证应该在提交前完成,而不是靠 redirect + flash 模拟“回显”。
最易被忽略的一点:模型验证错误只存在于当前请求周期内。如果你在控制器里调了 addError(),但没把模型对象传给视图,或用了 redirect() 而不是 render(),那些错误就彻底消失了,连 debug 都看不到。


















