在 Laravel 中添加 phone_zh 自定义验证规则,应在 AppServiceProvider 的 boot() 方法中用 Validator::extend() 注册正则规则 ^1[3-9]\d{9}$,并在 zh_CN/validation.php 中添加中文提示;切勿在 register() 中注册或继承 Validator 类。

怎么在 Laravel 里加一个叫 phone_zh 的自定义验证规则
直接往 App\Providers\AppServiceProvider 的 boot() 方法里注册最省事,不用动核心类,也避免升级冲突。Laravel 的 Validator::extend() 就是干这个的。
常见错误是把规则写在 register() 里——它还没加载验证器实例,会报 Call to a member function extend() on null。
- 用正则判断是否符合大陆手机号格式:
^1[3-9]\d{9}$ - 别忘了加中文提示:在
resources/lang/zh_CN/validation.php里补上'phone_zh' => '手机号格式不正确' - 如果规则要复用到多个项目,才值得单独抽成服务提供者;单项目没必要
为什么不要继承或重写 Illuminate\Validation\Validator
有人想通过 extends 原始 Validator 类来加方法,结果发现 $validator->phoneZh($attribute, $value) 调不通——因为 Laravel 内部用的是「规则名映射回调」机制,不是靠方法存在与否来触发的。
真正生效的前提只有两个:一是规则名被 extend() 注册过,二是验证时用了该规则名字符串(如 'mobile' => 'required|phone_zh')。
- 重写 Validator 类会导致
ValidatorFactory创建实例失败,尤其在队列任务或测试中容易静默崩溃 - 升级 Laravel 时,父类方法签名一变,你的子类立刻报
Declaration must be compatible - 所有自定义逻辑塞进闭包或独立类里,再用
extend()接入,才是官方推荐路径
validateWithBag() 和手动创建 Validator 实例的区别
控制器里写 $this->validate($request, [...]) 看似方便,但它默认抛异常、跳转回上一页,没法捕获后做定制处理(比如记录日志、返回 JSON 错误码)。这时候就得手动 new 一个 Validator。
关键点在于:手动实例化后,必须调用 fails() 或 passes() 才真正执行验证;只 new 不调用,规则压根不会跑。
- 获取错误信息用
$validator->errors()->all(),不是$validator->getMessageBag() - 想合并多个字段错误到一个 bag?传第三个参数:
Validator::make($data, $rules, [], ['mobile' => '手机号']) - 性能上没差别,但手动实例更可控——特别是 API 场景下需要统一返回结构时
规则里访问请求数据以外的上下文怎么办
比如验证「密码确认」要对比另一个字段,或「文章标题长度」要根据用户等级放宽限制——extend() 的回调函数第三个参数 $parameters 只能传固定值,不够用。
正确做法是用闭包绑定外部变量,或者改用 Validator::extendImplicit()(隐式规则),它允许你在回调里调用 $validator->getData() 和 $validator->getRules()。
- 别在闭包里直接 use
$request—— 它可能没经过中间件,$request->user()是 null - 需要用户信息?先从
Auth::user()拿,别依赖请求对象 - 规则里做 DB 查询要小心:验证失败时查询白跑了,建议只查缓存或配置项
真正麻烦的从来不是写规则本身,而是规则里混了业务逻辑又没分层——比如把「手机号是否已注册」塞进 phone_zh 里。验证规则只管格式,唯一性校验交给 Rule::unique() 或自定义 Rule 类更稳妥。











