扩展/重写 Laravel 验证器类

cjd*_*y13 3 php laravel laravel-validation laravel-7 laravel-8

在 Laravel 8.3 中,他们引入了一个新功能stopOnFirstFailure,一旦规则失败,就会完全停止验证。我想在Laravel 7中使用这个功能。在检查Laravel 8的vendor/laravel/framework/src/Validation/Validator.php时,我发现stopOnFirstFailure只是在Validator.phppasss函数中添加了一个if语句,如果受保护的变量stopOnFirstFailure为 true,则中断验证循环。是否可以通过扩展/覆盖 Validator.php 类在 Laravel 7 中实现这些?我一直在研究扩展 Laravel 核心类,并偶然发现了这篇文章,但它有点令人困惑,因为该文章只展示了如何重写特定函数。就我而言,我需要声明一个受保护的变量,覆盖一个函数并声明一个新函数。

Laravel 8 Validator.php 代码:

声明变量:

/**
     * Indicates if the validator should stop on the first rule failure.
     *
     * @var bool
     */
    protected $stopOnFirstFailure = false;
Run Code Online (Sandbox Code Playgroud)

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;
    }
Run Code Online (Sandbox Code Playgroud)

传递函数:

/**
     * 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;
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)

编辑:验证器在我的代码中通过表单请求使用。我的代码示例:

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);
    }
}

Run Code Online (Sandbox Code Playgroud)

编辑:遵循@thefallen的建议,这就是我所做的。我的 CustomValidator.php 类位于应用程序目录中的 CustomClass 中:

<?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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我的 ValidatorFactory 在 CustomClass 文件夹中

<?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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我的应用服务提供商:

<?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()
    {
        //
    }
}

Run Code Online (Sandbox Code Playgroud)

the*_*len 6

您基本上需要扩展验证器以对该方法进行更改,然后创建自己的验证工厂来创建这个新的验证器而不是默认的验证器。所以第一步是使用你自己的验证器:

use Illuminate\Validation\Validator;

class CustomValidator extends Validator
{
    public function passes()
    {
        //TODO make changes on that loop
        return parent::passes();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你需要一个验证工厂来创建这个新类,这也将扩展默认的类:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,app/Providers/AppServiceProvider.phpregister()方法中,您需要将默认工厂与您的自定义工厂交换:

$this->app->extend('validator', function () {
    return $this->app->get(ValidatorFactory::class);
});
Run Code Online (Sandbox Code Playgroud)

请注意,这validator是 的绑定(或别名)的名称Illuminate\Validation\Factory。您应该可以开始并能够对验证器进行任何更改。