一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

TP8.0 验证器自定义规则编写全攻略 保姆级

时间:2026-07-10 10:12:47 编辑:袖梨 来源:一聚教程网

ThinkPHP 8.0 中自定义验证规则需通过 init() 方法或控制器动态注册,手机号校验需支持国际区号参数、身份证校验须预加载省份码避免实时查库,场景校验必须正确定义 $scene 属性否则规则失效。

在 ThinkPHP 8.0 中为手机号加国际区号校验、为身份证号做末位校验、为密码强制要求含大小写字母和数字——这些不能靠内置规则解决的业务逻辑,必须写自定义验证规则,否则验证器直接报错或跳过校验。

注册自定义规则的两种入口

方法一:在验证器类的 init() 方法中注册(推荐)
打开 app/validate/UserValidate.php,在 init() 方法里调用 $this->extend(),传入别名和回调函数或方法名字符串。

方法二:在控制器中动态注册
创建验证器实例后,立即调用 $validate->extend(),适用于临时、一次性、不复用的规则,但无法被缓存复用,且无法参与场景(scene)校验。

【别名必须全局唯一】 若两个验证器都注册了 'id_card' 别名,后注册的会覆盖前一个,导致不可预期的校验行为。

写一个带参数的手机号校验规则

第一步:在 UserValidate 类的 init() 方法中注册闭包

$this->extend('isMobile', function($value, $rule) {
  $prefix = is_string($rule) ? $rule : ($rule[0] ?? '86');
  $pattern = match($prefix) {
    '86' => '/^1[3-9]d{9}$/u',
    '852' => '/^([2-9]d{7}|[2-9]d{3}-d{4})$/u',
    default => '/^+' . preg_quote($prefix) . 'd{10,15}$/u';
  };
  return (bool) preg_match($pattern, $value);
});

第二步:在 $rule 中使用带参写法

'mobile' => 'isMobile:86''mobile' => 'isMobile:852',注意冒号后不要加空格。

第三步:在 $message 中配提示语

'mobile.isMobile' => ':attribute 必须是有效的 :rule 区号手机号',其中 :rule 会自动替换为传入的参数值(如 86)。

这一步操作起来很简单,直接把文件拖进去就行。但若漏掉 u 修饰符,中文或 emoji 输入时正则会匹配失败,且错误无提示,只返回 false。

写一个查库类的身份证校验规则

先在 __construct() 中预加载合法省份码,避免每次验证都查库:

$this->validProvinces = Cache::remember('idcard_provinces', 3600, function () {
  return Db::name('province_code')->column('code');
});

再定义校验方法,签名必须完整:

public function checkIdCard($value, $rule = '', $data = [], $field = '') {
  if (!is_string($value) || strlen($value) !== 18) {
    return '身份证长度必须为18位';
  }
  $provinceCode = substr($value, 0, 2) . '00';
  if (!in_array($provinceCode, $this->validProvinces)) {
    return '身份证归属地代码无效';
  }
  // 末位校验略(实际需补全)
  return true;
}

最后在 init() 中注册别名:

$this->extend('id_card', 'checkIdCard');

【禁止在回调里实时查库】 若此处直接写 Db::name('idcard_check')->where(...)->find(),高并发下数据库连接池会迅速耗尽,QPS 断崖式下跌。

验证器中启用场景并绑定规则

① 在验证器类中声明 $scene 属性,例如:

protected $scene = [
  'login' => ['username', 'password'],
  'register' => ['username', 'password', 'mobile', 'id_card'],
];

② 控制器中调用时指定场景名:

$result = $validate->scene('register')->check($data);

③ 场景字段可叠加额外规则:

protected $scene = [
  'register' => ['username', 'password', 'mobile', 'id_card'],
];
protected $pattern = [
  'username' => '/^[a-zA-Z][a-zA-Z0-9_]{4,19}$/u',
];

若未定义 $scene,调用 scene('xxx') 不会报错,但规则完全不生效,数据直接穿透过去——这个坑没有任何日志提示,只能靠断点确认。

热门栏目