laravel validator 可在 service、job 等任意位置通过 validator::make() 手动调用,无需绑定控制器;validate() 失败抛异常,validated() 仅在校验通过后返回安全数据;unique 规则需显式指定连接名(如 tenant.users)并处理软删除。

不用专门“分离”验证逻辑,Laravel 的 Validator 本身就不绑定控制器 —— 直接在任何地方 new 或用 Validator::make() 就能用,所谓“独立验证”就是别写进 Controller 方法里硬编码。
怎么在 Service 或 Form Request 外手动调用 Validator
最常见需求:在 Service 类、Job、API 中间件或命令行里做数据校验,不走 FormRequest 流程。
- 用
Validator::make()是最轻量方式,返回Validator实例,可链式调用validate()(失败抛异常)或fails()(手动判断) - 别直接 new
Validator,它依赖容器注入的TranslationFinder和PresenceVerifier,手动 new 容易报错 - 如果需要复用规则,把规则数组抽成 private static 属性或 config 文件,别塞在方法里
// 在 app/Services/UserService.php 里
use Illuminate\Support\Facades\Validator;
public function createFromRawData(array $data)
{
$validator = Validator::make($data, [
'email' => 'required|email|unique:users',
'age' => 'nullable|integer|min:13',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
return User::create($validator->validated());
}
validate() 和 validated() 的关键区别
这两个方法看起来像一对,但行为差异直接影响错误处理和数据安全。
-
validate():立即校验,失败时抛出ValidationException(自动被 Laravel 异常处理器转成 422 响应),适合你不想自己处理失败逻辑的场景 -
validated():只在已校验通过的前提下返回过滤后的数据;如果还没调用validate()或校验失败就调,会返回空数组 —— 不报错,但数据丢了,这点容易被忽略 - 如果你用
fails()+errors()手动处理,记得最后一定要调validate()或确保校验已执行,否则validated()拿不到干净数据
自定义规则写在哪?别放 App\Rules 就完事
自定义规则不是“扔进 Rules 目录就自动可用”,注册和使用有隐含依赖。
- 类名必须符合 PSR-4,且实现
Rule接口(或继承AbstractRule),否则Validator::extend()会静默失败 - 如果用
Validator::extend('foo', ...)动态注册,务必在服务提供者(如AppServiceProvider@boot)里执行,不能在模型或控制器里临时注册 —— 否则测试时可能漏掉、队列里不可用 - 闭包形式的自定义规则无法序列化,放进 Job 或缓存后会报
Serialization of closure is not allowed
为什么有时候 unique 规则查不到数据库?
这不是规则本身问题,而是 unique 依赖 Laravel 的 DatabasePresenceVerifier,而它默认只连接 default 数据库连接。
- 如果你的用户表在
tenant连接上,但没显式指定,unique:users,email会去查default库,永远返回“可用” - 正确写法是
unique:tenant.users,email(注意中间是英文点号),或者用数组语法:['unique:users,email,NULL,id,tenant_id,' . $tenantId] - 软删除模型加
withTrashed()时,unique默认不包含 deleted_at 字段,得配合whereNull('deleted_at')手动加条件,否则已被软删的邮箱仍被判定为“已存在”
真正麻烦的从来不是写验证规则,而是验证上下文 —— 数据来源(HTTP?队列?命令?)、数据库连接、软删除状态、多租户隔离,这些不显式对齐,Validator 再灵活也救不了。










