必须将复杂校验逻辑抽离为独立验证类,如PhoneValidator需继承yii\validators\Validator、重写validateAttribute()、调用parent::init()、正确使用addError,并支持clientValidateAttribute()及rules中完整命名空间配置。

创建独立验证类文件
当校验逻辑涉及正则匹配、数据库查询、多字段协同或需被多个模型复用时,必须抽离为独立验证类。在common/validators目录下新建PhoneValidator.php文件(路径可按项目规范调整,但类名必须以Validator结尾)。
类必须继承yii\validators\Validator,且validateAttribute()方法是唯一被框架调用的入口——写validate()无效。
这一步漏掉parent::init()会导致$this->message等属性未初始化,后续报错信息为空或报PHP Notice。
编写核心验证逻辑
在PhoneValidator类中重写validateAttribute()方法:
先判断值是否为空,若需跳过空值则手动检查:if (empty($model->$attribute)) return; 否则直接执行校验;再用preg_match()匹配11位手机号格式,失败时调用$this->addError($model, $attribute, $this->message)。
【注意:addError第一个参数必须是$model对象,不是字符串】
若需支持客户端验证,必须同时实现clientValidateAttribute()并返回合法JS字符串,否则表单失去焦点时不触发校验。
声明并使用验证规则
在模型的rules()方法中添加规则:
[['mobile'], 'common\validators\PhoneValidator']
如需传参,先在验证类中声明public属性:public $strict = true;,再在rules中传入:[['mobile'], 'common\validators\PhoneValidator', 'strict' => false]。
验证类路径必须完整,不能省略命名空间;若类名不以Validator结尾,Yii自动加载将失败,validate()静默跳过该条规则。
配置when与skipOnEmpty行为
第一步:明确触发条件。使用when控制整条规则是否执行,例如仅当用户类型为'member'时才校验手机号:'when' => fn($m) => $m->userType === 'member'。
第二步:处理空值逻辑。若when返回true,再由skipOnEmpty决定是否跳过空值校验,默认为true;设为false可强制对空值也执行校验。
【when返回false时,skipOnEmpty完全不生效】
第三步:组合使用。常见安全组合是'when' => fn($m) => $m->status === 1, 'skipOnEmpty' => false,确保状态为1时无论字段是否为空都校验。


















