Laravel中复用验证规则优先用Rule::extends()或闭包,避免错误使用$this;自定义Rule类须实现passes()和message()方法;唯一性校验应依赖数据库约束而非模型查询。

在 FormRequest 中复用自定义验证规则,直接用 Rule 类或闭包就行
不用非得写个独立的 Rule 类——Laravel 的 Rule 工厂和匿名闭包就能快速复用,尤其适合项目里只在几个地方用、但逻辑又不想重复写的场景。
常见错误现象:ValidationException 报错说 “Class xxx does not exist”,其实是把自定义类名写错了路径,或者忘了 use Illuminate\Contracts\Validation\Rule;;更隐蔽的是闭包里用了 $this 却没意识到 FormRequest 的验证规则是静态上下文,闭包里拿不到实例属性。
- 推荐优先用
Rule::extends()注册一次,全局可用(比如邮箱格式校验带公司域名白名单) - 如果只是单个 FormRequest 里复用,直接在
rules()方法里写闭包,注意闭包参数顺序是($attribute, $value, $fail) - 闭包里不能访问
$this->input()或其他实例方法,需要提前提取数据:比如$password = $this->input('password');再传进闭包
写一个可复用的 Rule 类,别漏掉 passes() 和 message()
真正要跨多个请求、多个项目复用,才值得单独建类。但很多人只写了 passes(),结果报错 “Method message not found”,因为 Laravel 验证器强制要求这两个方法都存在。
使用场景:比如校验手机号是否已注册、密码强度是否含大小写字母+数字+符号、身份证号是否合法——这些逻辑稳定、会被多处调用。
- 类必须实现
Illuminate\Contracts\Validation\Rule接口 -
passes()返回布尔值,message()返回字符串(支持翻译:用__('validation.custom.phone_exists')) - 构造函数里接收参数(如
public function __construct(private string $table = 'users')),避免硬编码 - 注册到服务提供者时,用
Validator::extend('phone_exists', PhoneExistsRule::class);,但注意命名冲突:别叫exists,它已被 Laravel 占用
FormRequest 的 withValidator() 是补救现场,不是主战场
有人一上来就想在 withValidator() 里加规则,结果发现 $validator->sometimes() 不生效、或错误消息不显示在对应字段下——因为这个钩子是验证“启动后”才进来的,规则已经编译完了。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
性能影响:在这里动态加规则会绕过 Laravel 的规则预编译优化,且无法享受 required_if 这类依赖关系的自动处理。
- 只在需要“根据已有字段值决定是否启用某条规则”时用,比如:当
type是'company'时,才校验tax_id - 加规则用
$validator->addRules(['tax_id' => 'required|size:15']),别用after()做校验逻辑,那是给最终兜底用的 - 错误消息默认挂到
tax_id字段下,但如果想挂到type下,得手动$validator->errors()->add('type', '税务号缺失');
验证规则里调用模型方法?小心 N+1 和事务隔离问题
自定义规则里写 User::where(...)->exists() 看似简单,但容易在批量提交、高并发下出问题:比如两个请求同时校验同一邮箱是否可用,都查到“不存在”,然后都创建成功——违反唯一约束。
兼容性影响:MySQL 的 READ COMMITTED 隔离级别下,这种校验天然有竞态;PostgreSQL 更严,但也要配合 SELECT FOR UPDATE 才保险。
- 唯一性校验优先用数据库约束 +
unique规则,它底层走的是select count(*),虽不完美但够用 - 真要强一致性,把校验逻辑挪到控制器或 Job 里,在 DB transaction 中做
insert ... on conflict do nothing或捕获唯一索引异常 - 别在 Rule 类的
passes()里调用 Eloquent 关联方法(如$user->profile),关联查询可能被多次触发,且没做懒加载控制
复杂点在于规则的生命周期和执行时机不透明——它既不是纯函数式,也不完全属于请求上下文。最容易被忽略的是:闭包规则和 Rule 类规则的错误消息绑定机制不同,前者靠返回字符串,后者靠 message() 方法,混用时调试成本会陡增。










