Laravel 6验证规则类构造函数注入失败,因Validator直接new实例而未走服务容器;应改用__invoke方法+Validator::extend手动resolve依赖,或在rules()中使用闭包获取服务。

Laravel 6 的服务容器不能直接在自定义验证规则类的构造函数中注入依赖,因为验证器(Validator)默认用 new static() 实例化规则,完全绕过服务容器,DI 不生效。
为什么构造函数注入会失败
验证器不调用容器的 make() 方法来创建规则实例,而是直接 new —— 所以即使你写了 public function __construct(MyService $service),Laravel 也不会解析它,反而可能报错:Target [MyService] is not instantiable,尤其在执行 php artisan optimize:clear 后更明显。
推荐做法:用 __invoke + Validator::extend
让规则类实现可调用接口(__invoke),并在注册时手动从容器取服务:
- 定义规则类,不写构造函数依赖,只提供
__invoke($attribute, $value, $fail) - 在
AppServiceProvider@register()或单独的服务提供者中注册:
<?php
use Illuminate\Support\Facades\Validator;
use App\Rules\UniqueForCurrentUser;
public function boot()
{
Validator::extend('unique_for_user', function ($attribute, $value, $parameters, $validator) {
$rule = new UniqueForCurrentUser(resolve(MyService::class));
return $rule($attribute, $value, $validator->getFailureBag());
});
}
Form Request 中安全使用服务和 Auth
不要在 rules() 方法里直接调 Auth::user() 或查数据库 —— 这个方法在中间件前执行,用户可能未认证;且每次验证(包括重试)都会重复执行,引发 N+1 或权限漏洞。
- 把动态逻辑移到规则内部,在
__invoke中才访问auth()->user()或DB::table() - 若需复用,封装成独立服务类(如
UserPermissionChecker),规则只调它的方法 - 避免在规则里做耗时操作;必要时加缓存或延迟校验
替代方案:闭包规则(适合简单场景)
如果逻辑不复杂,直接在 rules() 返回数组中用闭包,运行时才取服务:
// 在 Form Request 的 rules() 方法中
return [
'email' => [
'required',
function ($attribute, $value, $fail) {
$service = app(EmailValidatorService::class);
if (!$service->isValid($value)) {
$fail(':attribute 格式不合法');
}
}
],
];


















