应通过场景(scenario)隔离验证逻辑,而非绕过required校验;yii2默认使用'default'场景且rules未限定on时,所有required规则对insert/update均生效。

更新数据时 required 校验触发,不是要“绕过”,而是没正确区分场景 —— 默认场景下所有 required 规则都会生效,包括更新操作。
为什么 update 时还校验 required 字段?
Yii2 的 validate() 不区分 insert/update,默认走当前 scenario 下的全部激活规则。如果你没显式切换场景,模型用的是 'default' 场景,而 required 规则又没限定 on,那它就会对所有操作生效。
- 常见错误:直接在控制器里写
$model->load($post) && $model->save(),却没设场景 - 更隐蔽的问题:rules() 里写了
[['name'], 'required'],但编辑表单根本没传name字段(比如前端只渲染了可改字段),结果load()后name变成null,required立刻报错 - 注意:
safe不等于跳过验证 —— 它只是允许字段被load()接收,不参与验证;但required是验证规则,和safe无关
正确做法:用场景(scenario)隔离验证逻辑
让创建和更新走不同的规则集,而不是删掉或注释 required。
- 在 Model 中定义两个场景:
public function scenarios() { return [ 'create' => ['name', 'email', 'password'], 'update' => ['name', 'email'], ]; } - 在
rules()中绑定场景:[['name', 'email'], 'required', 'on' => ['create', 'update']],[['password'], 'required', 'on' => 'create'] - 控制器中显式指定:
if ($model->load($request->post(), '') && $model->scenario = 'update') { ... }或更推荐:$model->setScenario('update')再 load
特殊情况:某个字段更新时允许为空,但创建时必填
不能靠 skipOnEmpty 来“蒙混过关”,它控制的是值为空时是否跳过该条规则,而不是让规则失效。
- 错误写法:
[['phone'], 'required', 'skipOnEmpty' => true]→ 这会让空字符串、null都跳过校验,连创建时也失效 - 正确写法:用场景 + 条件规则,例如
[['phone'], 'required', 'on' => 'create'],更新时根本不挂这条规则 - 如果字段语义上“更新可空、创建必填”,但又不想拆场景,可用内联验证器手动判断:
[['phone'], function($attribute) { if ($this->scenario === 'create' && empty($this->$attribute)) { $this->addError($attribute, '手机号不能为空'); } }]
容易被忽略的坑:load() 没传第二参数导致字段丢失
$model->load($data) 默认只加载 formName() 下的数据,如果表单没套 FormName[xxx] 结构,或者你用了空字符串 form name,load() 会静默丢弃所有字段 —— 此时 required 报错,其实是因为字段压根没进模型。
- 检查是否用了
ActiveForm::begin(['id' => 'xxx'])但没配Model::formName() - 调试时加一句:
var_dump(array_keys($model->getDirtyAttributes()));看哪些字段实际被 load 进来了 - 更稳妥:用
$model->load($data, '')强制按扁平结构加载,前提是前端字段名和属性名一致











