Extending/Overriding Laravel 验证者 Class
Extending/Overriding Laravel Validator Class
在 Laravel 8.3 中,他们引入了一项新功能 stopOnFirstFailure,一旦规则失败,该功能将完全停止验证。我想在 Laravel 7 中使用此功能。在查看 Laravel 8 的 vendor/laravel/framework/src/Validation/Validator.php 后,我发现 stopOnFirstFailure只是在passes函数中添加了一个if语句Validator.php 如果受保护的变量 stopOnFirstFailure 是真的。是否可以通过 extending/overiding Validator.php class 在 Laravel 7 中实现这些?我一直在研究扩展核心 Laravel classes 并偶然发现了这个 article 但它有点令人困惑,因为这篇文章只展示了如何覆盖特定的功能。在我的例子中,我需要声明一个受保护的变量,覆盖一个函数并声明一个新函数。
Laravel 8Validator.php代码:
声明变量:
/**
* Indicates if the validator should stop on the first rule failure.
*
* @var bool
*/
protected $stopOnFirstFailure = false;
stopOnFirstFailure 函数:
/**
* Instruct the validator to stop validating after the first rule failure.
*
* @param bool $stopOnFirstFailure
* @return $this
*/
public function stopOnFirstFailure($stopOnFirstFailure = true)
{
$this->stopOnFirstFailure = $stopOnFirstFailure;
return $this;
}
通过函数:
/**
* Determine if the data passes the validation rules.
*
* @return bool
*/
public function passes()
{
$this->messages = new MessageBag;
[$this->distinctValues, $this->failedRules] = [[], []];
// We'll spin through each rule, validating the attributes attached to that
// rule. Any error messages will be added to the containers with each of
// the other error messages, returning true if we don't have messages.
foreach ($this->rules as $attribute => $rules) {
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
continue;
}
if ($this->stopOnFirstFailure && $this->messages->isNotEmpty()) {
break;
}
foreach ($rules as $rule) {
$this->validateAttribute($attribute, $rule);
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
break;
}
if ($this->shouldStopValidating($attribute)) {
break;
}
}
}
编辑:表单请求在我的代码中使用了验证器。
我的代码示例:
class UpdateRegistrationTagsRequest extends FormRequest
{
protected $stopOnFirstFailure = true;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'product_id' => ['required'],
'product.*.type' => ['required','distinct'],
'product.*.value' => ['required'],
'product' => ['bail', 'required', 'array', new CheckIfArrayOfObj, new CheckIfProductTypeExists($this->product_id)]
];
}
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
$response = new JsonResponse(['api' => [
'header' => [
'message' => 'The given data is invalid',
'errors' => $validator->errors()->first()
],
'body' => ''
]], 422);
throw new \Illuminate\Validation\ValidationException($validator, $response);
}
}
编辑:遵循@thefallen 的建议,这就是我所做的。
我的 CustomValidator.php class in CustomClass inside app directory:
<?php
namespace App\CustomClass;
use Illuminate\Validation\Validator;
use Illuminate\Support\MessageBag;
class CustomValidator extends Validator
{
/**
* Indicates if the validator should stop on the first rule failure.
*
* @var bool
*/
protected $stopOnFirstFailure = true;
/**
* Instruct the validator to stop validating after the first rule failure.
*
* @param bool $stopOnFirstFailure
* @return $this
*/
public function stopOnFirstFailure($stopOnFirstFailure = true)
{
$this->stopOnFirstFailure = $stopOnFirstFailure;
return $this;
}
/**
* Determine if the data passes the validation rules.
*
* @return bool
*/
public function passes()
{
$this->messages = new MessageBag;
[$this->distinctValues, $this->failedRules] = [[], []];
// We'll spin through each rule, validating the attributes attached to that
// rule. Any error messages will be added to the containers with each of
// the other error messages, returning true if we don't have messages.
foreach ($this->rules as $attribute => $rules) {
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
continue;
}
if ($this->stopOnFirstFailure && $this->messages->isNotEmpty()) {
break;
}
foreach ($rules as $rule) {
$this->validateAttribute($attribute, $rule);
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
break;
}
if ($this->shouldStopValidating($attribute)) {
break;
}
}
}
return parent::passes();
}
}
CustomClass 文件夹中的我的 ValidatorFactory
<?php
namespace App\CustomClass;
use Illuminate\Validation\Factory;
use App\CustomClass\CustomValidator;
class ValidatorFactory extends Factory
{
protected function resolve( array $data, array $rules, array $messages, array $customAttributes )
{
if (is_null($this->resolver)) {
return new CustomValidator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
我的 AppServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\CustomClass\ValidatorFactory;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->extend('validator', function () {
return $this->app->get(ValidatorFactory::class);
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
您基本上需要扩展 Validator 以对该方法进行更改,然后创建您自己的 Validation Factory 来创建这个新的 Validator 而不是默认的 Validator。所以第一步与你自己的验证器:
use Illuminate\Validation\Validator;
class CustomValidator extends Validator
{
public function passes()
{
//TODO make changes on that loop
return parent::passes();
}
}
然后你需要一个验证工厂来创建这个新的class,这也将扩展默认的:
use Illuminate\Validation\Factory;
class ValidatorFactory extends Factory
{
protected function resolve( array $data, array $rules, array $messages, array $customAttributes )
{
if (is_null($this->resolver)) {
return new CustomValidator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
最后在 app/Providers/AppServiceProvider.php
中的 register()
方法中,您需要将默认工厂与自定义工厂交换:
$this->app->extend('validator', function () {
return $this->app->get(ValidatorFactory::class);
});
请注意 validator
是 Illuminate\Validation\Factory
的绑定(或别名)的名称。您应该可以开始并能够对验证器进行任何更改。
可能有点晚了,但我在 Laravel 6 上遇到了同样的问题。
不想 extend/override 验证器的当前正常行为。
所以我这样做了
public function validateResolved()
{
if (!$this->authorize()) {
$this->failedAuthorization();
}
foreach ($this->rules() as $key=>$val) {
$validator = app('validator')->make(
request()->all(),
[$key=>$val],
$this->messages()
);
if ($validator->fails()) {
throw \Illuminate\Validation\ValidationException::withMessages($validator->errors()->messages());
}
}
}
在 Laravel 8.3 中,他们引入了一项新功能 stopOnFirstFailure,一旦规则失败,该功能将完全停止验证。我想在 Laravel 7 中使用此功能。在查看 Laravel 8 的 vendor/laravel/framework/src/Validation/Validator.php 后,我发现 stopOnFirstFailure只是在passes函数中添加了一个if语句Validator.php 如果受保护的变量 stopOnFirstFailure 是真的。是否可以通过 extending/overiding Validator.php class 在 Laravel 7 中实现这些?我一直在研究扩展核心 Laravel classes 并偶然发现了这个 article 但它有点令人困惑,因为这篇文章只展示了如何覆盖特定的功能。在我的例子中,我需要声明一个受保护的变量,覆盖一个函数并声明一个新函数。
Laravel 8Validator.php代码:
声明变量:
/**
* Indicates if the validator should stop on the first rule failure.
*
* @var bool
*/
protected $stopOnFirstFailure = false;
stopOnFirstFailure 函数:
/**
* Instruct the validator to stop validating after the first rule failure.
*
* @param bool $stopOnFirstFailure
* @return $this
*/
public function stopOnFirstFailure($stopOnFirstFailure = true)
{
$this->stopOnFirstFailure = $stopOnFirstFailure;
return $this;
}
通过函数:
/**
* Determine if the data passes the validation rules.
*
* @return bool
*/
public function passes()
{
$this->messages = new MessageBag;
[$this->distinctValues, $this->failedRules] = [[], []];
// We'll spin through each rule, validating the attributes attached to that
// rule. Any error messages will be added to the containers with each of
// the other error messages, returning true if we don't have messages.
foreach ($this->rules as $attribute => $rules) {
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
continue;
}
if ($this->stopOnFirstFailure && $this->messages->isNotEmpty()) {
break;
}
foreach ($rules as $rule) {
$this->validateAttribute($attribute, $rule);
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
break;
}
if ($this->shouldStopValidating($attribute)) {
break;
}
}
}
编辑:表单请求在我的代码中使用了验证器。 我的代码示例:
class UpdateRegistrationTagsRequest extends FormRequest
{
protected $stopOnFirstFailure = true;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'product_id' => ['required'],
'product.*.type' => ['required','distinct'],
'product.*.value' => ['required'],
'product' => ['bail', 'required', 'array', new CheckIfArrayOfObj, new CheckIfProductTypeExists($this->product_id)]
];
}
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
$response = new JsonResponse(['api' => [
'header' => [
'message' => 'The given data is invalid',
'errors' => $validator->errors()->first()
],
'body' => ''
]], 422);
throw new \Illuminate\Validation\ValidationException($validator, $response);
}
}
编辑:遵循@thefallen 的建议,这就是我所做的。 我的 CustomValidator.php class in CustomClass inside app directory:
<?php
namespace App\CustomClass;
use Illuminate\Validation\Validator;
use Illuminate\Support\MessageBag;
class CustomValidator extends Validator
{
/**
* Indicates if the validator should stop on the first rule failure.
*
* @var bool
*/
protected $stopOnFirstFailure = true;
/**
* Instruct the validator to stop validating after the first rule failure.
*
* @param bool $stopOnFirstFailure
* @return $this
*/
public function stopOnFirstFailure($stopOnFirstFailure = true)
{
$this->stopOnFirstFailure = $stopOnFirstFailure;
return $this;
}
/**
* Determine if the data passes the validation rules.
*
* @return bool
*/
public function passes()
{
$this->messages = new MessageBag;
[$this->distinctValues, $this->failedRules] = [[], []];
// We'll spin through each rule, validating the attributes attached to that
// rule. Any error messages will be added to the containers with each of
// the other error messages, returning true if we don't have messages.
foreach ($this->rules as $attribute => $rules) {
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
continue;
}
if ($this->stopOnFirstFailure && $this->messages->isNotEmpty()) {
break;
}
foreach ($rules as $rule) {
$this->validateAttribute($attribute, $rule);
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
break;
}
if ($this->shouldStopValidating($attribute)) {
break;
}
}
}
return parent::passes();
}
}
CustomClass 文件夹中的我的 ValidatorFactory
<?php
namespace App\CustomClass;
use Illuminate\Validation\Factory;
use App\CustomClass\CustomValidator;
class ValidatorFactory extends Factory
{
protected function resolve( array $data, array $rules, array $messages, array $customAttributes )
{
if (is_null($this->resolver)) {
return new CustomValidator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
我的 AppServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\CustomClass\ValidatorFactory;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->extend('validator', function () {
return $this->app->get(ValidatorFactory::class);
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
您基本上需要扩展 Validator 以对该方法进行更改,然后创建您自己的 Validation Factory 来创建这个新的 Validator 而不是默认的 Validator。所以第一步与你自己的验证器:
use Illuminate\Validation\Validator;
class CustomValidator extends Validator
{
public function passes()
{
//TODO make changes on that loop
return parent::passes();
}
}
然后你需要一个验证工厂来创建这个新的class,这也将扩展默认的:
use Illuminate\Validation\Factory;
class ValidatorFactory extends Factory
{
protected function resolve( array $data, array $rules, array $messages, array $customAttributes )
{
if (is_null($this->resolver)) {
return new CustomValidator($this->translator, $data, $rules, $messages, $customAttributes);
}
return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
}
最后在 app/Providers/AppServiceProvider.php
中的 register()
方法中,您需要将默认工厂与自定义工厂交换:
$this->app->extend('validator', function () {
return $this->app->get(ValidatorFactory::class);
});
请注意 validator
是 Illuminate\Validation\Factory
的绑定(或别名)的名称。您应该可以开始并能够对验证器进行任何更改。
可能有点晚了,但我在 Laravel 6 上遇到了同样的问题。 不想 extend/override 验证器的当前正常行为。 所以我这样做了
public function validateResolved()
{
if (!$this->authorize()) {
$this->failedAuthorization();
}
foreach ($this->rules() as $key=>$val) {
$validator = app('validator')->make(
request()->all(),
[$key=>$val],
$this->messages()
);
if ($validator->fails()) {
throw \Illuminate\Validation\ValidationException::withMessages($validator->errors()->messages());
}
}
}