自定义验证不触发的核心原因是验证流程未走通:数据未加载、场景不匹配、空值被跳过或规则未挂载。需确保load()成功、scenario三处统一配置、自定义规则设skiponempty/skiponerror为false,并正确使用addrule()。

自定义验证不触发,核心问题往往不在规则写法本身,而在于验证流程没走通——数据没加载、场景没对上、空值被跳过、或规则根本没挂到当前实例。下面按高频原因逐条说明。
数据压根没进模型,validate()只是白跑
调用 $model->validate() 前,必须先让数据进模型。Yii 不会自动从请求体取数据,得手动 load():
- 表单提交(
application/x-www-form-urlencoded):用$model->load($request->post()) - JSON 提交(
application/json):先解析原始体:$data = Json::decode($request->getRawBody()); $model->load($data, '') - 字段名不带模型前缀(比如发的是
{"username":"a"}而不是{"loginform":{"username":"a"}}),load()第二个参数必须传空字符串'',否则匹配失败返回false -
关键检查点:在
load()后加一行var_dump($model->attributes);,确认字段值真出现在模型里
场景(scenario)设了但规则不生效
Yii 的 rules 是按场景过滤执行的,三处必须同时配齐:
- 模型中重写
scenarios(),明确列出该场景允许参与验证的字段,例如:return ['create' => ['name', 'email']];。不写这个,字段连加载都不加载,更别说校验 - rules 中对应规则要加
'on' => 'create',比如:['email', 'required', 'on' => 'create'] - 控制器里必须显式设置:
$model->scenario = 'create';,且这行要在load()和validate()之间
自定义方法规则被跳过:空值或已有错误拦住了它
默认情况下,如果字段为空,或前面某条规则已报错,Yii 会跳过后续验证(包括你写的 validateXxx() 方法)。想让它一定执行,就得关掉跳过机制:
- 在 rules 条目里加上:
'skipOnEmpty' => false, 'skipOnError' => false - 例如:
['status', 'validateStatus', 'skipOnEmpty' => false, 'skipOnError' => false] - 同时,在自定义方法内部也要主动判断空值逻辑,比如:
if ($this->$attribute === null || $this->$attribute === '') { return; },避免无意义校验
动态加规则却没触发:addRule() 用法不对
用 $model->addRule() 添加规则时,容易踩三个坑:
- 必须指定
'on' => 'xxx',且后面要立刻设置$model->scenario = 'xxx',否则规则不会激活 - addRule 只对当前模型实例有效,不能复用到其他实例;ActiveRecord::findOne() 加载的模型,validators 是空的,得 new 一个干净实例再加
- 不要在
rules()方法里写运行时判断(如if ($this->type === 'vip')),因为rules()是类加载时静态执行的,$this 还不存在。应改用beforeValidate()方法中动态 addRule











