yii场景验证生效需同时满足三条件:一、模型中重写scenarios()返回字段白名单;二、rules()中规则显式声明on参数指定场景;三、控制器中主动设置$model->scenario='xxx',且load()返回值必须校验。

场景名写了但 rules() 里没配 on,规则压根不跑
rules() 中某条规则若没写 on 参数,它默认只在 default 场景下生效;哪怕你控制器里写了 $model->scenario = 'update',这条规则也不会触发。
常见错误是:以为只要定义了场景、设置了 scenario,所有规则就自动按场景过滤——其实 Yii 只过滤「明确声明了 on」的那部分规则。
- 正确写法:
[['title'], 'required', 'on' => ['create', 'update']]或[['status'], 'in', 'range' => [0,1], 'on' => 'admin_update'] - 错误写法:
[['title'], 'required']—— 它永远只属于default场景,跟create无关 - 想让某条规则通吃所有场景?留空
on不行,得显式写'on' => ['default', 'create', 'update']
scenarios() 没重写,字段连加载都进不来
scenarios() 控制的是「哪些字段能被 load() 接收并赋值」,不是控制验证。漏掉这步,字段根本进不到模型属性里,validate() 就算跑十遍也查不到数据。
比如你定义了 'create' => ['name', 'email'],但前端 POST 传了 phone 字段,$model->load($post) 后 $model->phone 仍是 null —— 即使 rules() 里写了 ['phone', 'required', 'on' => 'create'],也因字段未加载而跳过校验。
- 必须重写
scenarios()方法,返回带字段白名单的数组 - 不要依赖父类默认实现(它只放行
default场景) - 敏感字段如
is_admin、created_by别放进任何scenarios()的字段列表里
load() 返回 false 却没检查,验证直接失效
$model->load() 失败时返回 false,但很多人只写 $model->load($post); $model->validate();,结果 load() 没成功,validate() 在空模型上跑,静默通过。
典型失败原因包括:表单字段没加 model 前缀(如 LoginForm 应传 loginform[username])、JSON 请求没手动解析、字段不在 scenarios() 白名单中。
- 必须判断
load()返回值:if (!$model->load($post)) { return $this->refresh(); } - JSON 请求要先
$raw = $request->getRawBody()→$data = Json::decode($raw)→$model->load($data, '') - 前端发扁平数据(如
{username: "a"})时,load()第二个参数必须传空字符串:$model->load($data, '')
动态改规则却忘了绑定场景
用 addRule() 添加规则后,若不显式设置 $model->scenario,新规则不会参与验证——因为 addRule() 默认只挂到 default 场景,除非你带 on 参数。
更隐蔽的问题是:ActiveRecord 实例(如 User::findOne(1))的 validators 属性初始为空,addRule() 能生效,但如果你在 beforeValidate() 里反复调用,可能重复添加同一规则。
- 动态加规则后,立刻设场景:
$model->addRule('price', 'required', ['on' => 'vip']); $model->scenario = 'vip'; - 避免在
beforeValidate()中无条件addRule(),先检查是否已存在:if (!$this->hasValidator('price')) { ... } - 旧版本 Yii(addRule() 的
on参数,得手动操作$model->validators数组
scenarios() 和 load() 返回值检查这两环。验证“看起来没报错”,往往不是规则写错了,而是字段压根没进来、或场景没对上。











