用 make:rule 创建可复用验证类是最干净、易测试的方式,生成类含 passes()(返回布尔值)和 message()(返回提示字符串),调用需 new 实例化,如 ['required', new ValidIdCard]。

怎么用 make:rule 创建可复用的验证类
这是最干净、最易测试的方式,适合需要在多个地方复用的规则(比如手机号、身份证号、密码强度)。生成的类自带 passes() 和 message() 方法,逻辑隔离、不污染全局。
- 执行命令:
php artisan make:rule ValidIdCard,会在app/Rules/ValidIdCard.php生成类 -
passes()中写校验逻辑,注意返回true表示通过,false触发错误;不要用return语句提前退出而不返回布尔值 -
message()返回字符串即可,支持使用$this->attribute和$this->value(需在passes()中赋值)动态拼提示,例如return $this->attribute . ' 不是有效的身份证号'; - 调用时必须用
new ValidIdCard实例化:'id_card' => ['required', new ValidIdCard],不能写成字符串'id_card' => 'required|valid_id_card'
为什么 Validator::extend() 必须放在 AppServiceProvider::boot()
因为 Validator 实例是在服务容器启动后期才构建的,register() 阶段它还不存在——否则会报 Call to a member function extend() on null。
- 只在
boot()里注册:Validator::extend('phone_zh', function ($attribute, $value, $parameters, $validator) { return preg_match('/^1[3-9]\d{9}$/', $value); }); - 配套要在
resources/lang/zh_CN/validation.php的'custom'数组里加提示:'phone_zh' => ':attribute 格式不正确' - 规则名(如
phone_zh)不能含空格或特殊字符,且必须全小写;若规则带参数(如min_length:8),闭包第 3 个参数$parameters是数组['8'] - 这种注册方式让规则能以字符串形式被所有验证器识别,但无法访问请求全部数据(比如跨字段比较)
内联闭包验证怎么写才不出错
适合单次、轻量、不跨上下文的校验,比如“邮箱不能是公司域名”“两次输入密码一致但不用 confirmed 规则”。
- 闭包必须接收三个参数:
($attribute, $value, $fail),其中$fail()是触发错误的唯一方式,不能用return false或抛异常 - 错误消息直接传给
$fail('提示文字'),它会自动绑定到当前字段,无需手动指定键名 - 不能在闭包里调用
$request->all()或依赖外部变量(除非use明确引入),否则在 FormRequest 的rules()中可能因作用域问题失效 - 示例:
'email' => ['required', 'email', function ($attribute, $value, $fail) { if (str_ends_with($value, '@example.com')) { $fail('禁止使用 example.com 邮箱'); } }]
自定义错误消息的优先级和键名怎么配
消息生效顺序:内联 $messages 数组 > FormRequest 的 messages() 方法 > 语言文件 validation.php。键名写错就 fallback 到默认提示,非常隐蔽。
- 键名格式严格为
字段名.规则名,比如'password.required'、'password.confirmed'、'email.unique',大小写和点号缺一不可 - 如果用了自定义规则(如
new ValidIdCard),它的message()方法优先级高于语言文件,但低于内联$messages - 内联
$messages必须作为validate()第三个参数传入:$request->validate($rules, $messages);漏掉这个参数,自定义消息完全不生效 - FormRequest 中的
messages()返回数组,键名一样是字段名.规则名,但注意:如果规则是数组形式(如['required', new StrongPassword]),对应键名仍是'password.required',不是'password.strong_password'
passes() 返回 false → 看错误是否出现,再逐步加逻辑。


















