Yii2 的 scenario 是验证逻辑的“上下文控制器”,通过精准定义字段集与 on/except 规则,实现注册、编辑等场景的自动过滤与验证;推荐合并父类 scenarios() 并优先构造时指定场景。

Yii2 的 scenario 不是开关,而是验证逻辑的“上下文控制器”。用得好,能让同一模型在注册、编辑、重置密码等不同业务动作中,自动启用精准字段集和对应规则,避免冗余判断和硬编码分支。
场景定义:继承 default + 显式扩展
别直接覆盖 scenarios(),否则会丢失父类默认行为(比如 SCENARIO_DEFAULT)。推荐写法是合并父类结果:
return array_merge(parent::scenarios(), [ 'register' => ['username', 'email', 'password'], 'profile_update' => ['username', 'email', 'avatar'], ])- 这样既保留了 default 场景的全部属性,又为新场景划定了明确的可操作字段范围
- 字段列表越精确,后续 load() 自动过滤越干净——没列进来的字段,哪怕 POST 里传了也不会被赋值
规则绑定:on/except 比场景名更灵活
rules() 中的 on 和 except 是细粒度控制的关键。它们不依赖是否定义了 scenarios(),只看当前 model->scenario 值是否匹配:
-
['email', 'required', 'on' => ['register', 'reset_password']]—— 多场景复用同一条规则 -
['password', 'string', 'min' => 8, 'except' => 'profile_update']—— 默认生效,仅排除某场景 - 没加
on或except的规则,始终生效(属于 default 场景兜底)
控制器调用:三种方式,推荐构造时指定
设置场景越早,越能避免中间环节误用 default:
- 构造时指定:
$model = new User(['scenario' => 'register'])—— 最清晰、无副作用 - 实例后设置:
$model->setScenario('update')—— 语义明确,支持链式调用 - 直接赋值:
$model->scenario = 'login'—— 简洁但易被忽略,不推荐在复杂流程中使用
验证与赋值联动:load() 自动按场景过滤
调用 $model->load($post) 时,Yii2 会自动依据当前 scenario,只把白名单字段从 POST 数据中提取并赋值。这意味着:
- 即使前端传了
id和created_at,只要它们不在当前 scenario 字段列表里,就不会被写入模型属性 - 配合 rules() 中的
safe规则,可进一步控制哪些字段允许批量赋值(防止 mass-assignment 漏洞) - 不用再手动
unset()敏感字段,逻辑更内聚、更安全


















