美文网首页工作生活
Laravel 使用 FormRequest 自定义表单验证类,

Laravel 使用 FormRequest 自定义表单验证类,

作者: 噫那里有条咸鱼 | 来源:发表于2019-07-01 15:38 被阅读0次

    需求:在我们的laravel5.7项目中,已经对错误返回做了一层封装,后来发现验证这一块越来越臃肿,所以需要单独的表单验证类来进行处理,就先到了使用FormRequest,但是使用FormRequest后的错误异常被框架直接抛出处理了,最后的重点就落在重写错误返回了。
    解决思路:既然框架捕捉了对应的异常,那么我们就在框架捕捉之前,抛出自己的异常,并做格式处理后返回。

    参考:这篇文章给予了思路 https://www.jianshu.com/p/658f979abfb7


    • 首先我们之前的错误返回封装
      传入对应的规则,然后调用Validator::make验证,有错误就抛出对应的异常,然后在 Exceptions中做对应处理以及错误提示国际化
        /**
         * 验证输入信息
         * @param $rules
         * @return bool
         * @throws SfoException
         */
        public function validateInput($rules)
        {
            $requests = $this->request->all();
    
            $validator = Validator::make($requests, $rules);
            if ($validator->fails()) {
                throw new SfoException(40013, ['params' => $validator->messages()->first()]);
            } else {
                $this->validated = array_intersect_key($requests, $rules);
                return false;
            }
        }
    

    关于FormRequest如何处理异常的,参考文章这一块写得不错,就直接引用过来了,懒得再写:

    作者: 该叶无法找到
    链接:https://www.jianshu.com/p/658f979abfb7
    来源:简书

    通常来说,Laravel中的错误都是异常(Exception),我们都可以在app\Exceptions\handler.php中进行统一处理。Form Request确实也抛出了一个Illuminate\Http\Exception\HttpResponseException异常,但这个异常是在路由逻辑中就被特殊处理了。
    首先我们来看看Form Request是如何被执行的:
    Illuminate\Validation\ValidationServiceProvider:

    <?php
    
    namespace Illuminate\Validation;
    
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Contracts\Validation\ValidatesWhenResolved;
    
    class ValidationServiceProvider extends ServiceProvider
    {
        /**
         * Register the service provider.
         *
         * @return void
         */
        public function register()
        {
            $this->registerValidationResolverHook(); // 看我看我看我
    
            $this->registerPresenceVerifier();
    
            $this->registerValidationFactory();
        }
    
        /**
         * Register the "ValidatesWhenResolved" container hook.
         *
         * @return void
         */
        protected function registerValidationResolverHook() // 对,就是我
        {
            // 这里可以看到对`ValidatesWhenResolved`的实现做了一个监听
            $this->app->afterResolving(function (ValidatesWhenResolved $resolved) {
                $resolved->validate(); // 然后调用了它的`validate`方法进行验证
            });
        }
    // ...
    

    你猜对了,Form Request就实现了这个Illuminate\Contracts\Validation\ValidatesWhenResolved接口:

    <?php 
    
    namespace Illuminate\Foundation\Http;
    
    use Illuminate\Http\Request;
    use Illuminate\Http\Response;
    use Illuminate\Http\JsonResponse;
    use Illuminate\Routing\Redirector;
    use Illuminate\Container\Container;
    use Illuminate\Contracts\Validation\Validator;
    use Illuminate\Http\Exception\HttpResponseException;
    use Illuminate\Validation\ValidatesWhenResolvedTrait;
    use Illuminate\Contracts\Validation\ValidatesWhenResolved; // 是你
    use Illuminate\Contracts\Validation\Factory as ValidationFactory;
    
    // 我们`app\Http\Requests\Request`便是继承于这个`FormRequest`类
    class FormRequest extends Request implements ValidatesWhenResolved // 就是你
    {
        use ValidatesWhenResolvedTrait; // 这个我们待会儿也要看看
    
        // ...
    

    FormRequest基类中的validate方法是由这个Illuminate\Validation\ValidatesWhenResolvedTrait实现的:
    Illuminate\Validation\ValidatesWhenResolvedTrait:

    <?php
    
    namespace Illuminate\Validation;
    
    use Illuminate\Contracts\Validation\ValidationException;
    use Illuminate\Contracts\Validation\UnauthorizedException;
    
    /**
     * Provides default implementation of ValidatesWhenResolved contract.
     */
    trait ValidatesWhenResolvedTrait
    {
        /**
         * Validate the class instance.
         *
         * @return void
         */
        public function validate() // 这里实现了`validate`方法
        {
            $instance = $this->getValidatorInstance(); // 这里获取了`Validator`实例
    
            if (! $this->passesAuthorization()) {
                $this->failedAuthorization(); // 这是调用了访问授权的失败处理
            } elseif (! $instance->passes()) {
                $this->failedValidation($instance); // 这里调用了验证失败的处理,我们主要看这里
            }
        }
    
        // ...
    

    在validate里,如果验证失败了就会调用$this->failedValidation(),继续:
    Illuminate\Foundation\Http\FormRequest:

    <?php
    
    // ...
    
        /**
         * Handle a failed validation attempt.
         *
         * @param  \Illuminate\Contracts\Validation\Validator  $validator
         * @return mixed
         */
        protected function failedValidation(Validator $validator)
        {
            throw new HttpResponseException($this->response( // 这里抛出了传说中的异常
                $this->formatErrors($validator)
            ));
        }
    

    终于看到异常了!可是这个异常在另一个地方被处理了:


    好了既然知道异常来源了,那么剩下来的就好办了:
    1、重写failedValidation方法
    我是在app\http\Requests下创建了NewFormRequest类并重写failedValidation方法,抛出我需要的异常:

    <?php
    /**
     * Created by PhpStorm.
     * User: 有梦想的咸鱼
     * Date: 2019/7/1
     * Time: 14:38
     */
    
    namespace App\Http\Requests;
    
    use App\Exceptions\SfoException;
    use Illuminate\Foundation\Http\FormRequest;
    use Illuminate\Contracts\Validation\ValidatesWhenResolved;
    use Illuminate\Contracts\Validation\Validator;
    
    /**
     * 本类重写了 FormRequest 类的错误返回,原继承FormRequest,创建的独立表单验证类将继承本类
     * Class NewFormRequest
     * @package App\Http\Requests
     */
    class NewFormRequest extends FormRequest implements ValidatesWhenResolved
    {
        /**
         * 重写框架的 FormRequest 类的 failedValidation 方法
         * @param Validator $validator
         * @throws SfoException
         */
        protected function failedValidation(Validator $validator)
        {
            throw new SfoException(40013, ['params' => $validator->messages()->first()]);
        }
    }
    

    2、生成自定义表单验证类继承于刚才写的NewFormRequest

    php artisan make:request CreateContractRequest
    

    执行后会在app/Http/Requests/生成CreateContractRequest.php:

    <?php
    
    namespace App\Modules\Business\Requests;
    
    use App\Http\Requests\NewFormRequest;
    use App\Modules\Core\Http\Controllers\ValidateDataRule;
    
    class CreateContractRequest extends NewFormRequest
    {
        /**
         * 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 [
                //签订人邮箱
                'signer_email'                         => 'nullable|string|max:100|email',
                //签订时间
                'sign_time'                            => 'required|date_format:Y-m-d'
    
                。。。。省略50行
            ];
        }
    }
    

    3、修改新的调用方式

    • 项目原来的调用方式:
        //创建合同
        public function createContract(Request $request)
        {
            $rules = [
                //签订人邮箱
                'signer_email' => 'nullable|string|max:100|email',
                //签订时间
                'sign_time'    => 'required|date_format:Y-m-d'
    
                //            。。。。省略50行
            ];
            if ($error = $this->validateInput($rules)) {
                return $error;
            }
            $input = $this->validated;
            ......
        }
    
    • 返回的错误:
    {
        "error_code": 40013,
        "error_desc": "签订时间格式错误",
        "tag": "operator7"
    }
    
    • 新的调用方式:通过依赖注入CreateContractRequest
        //创建合同
        public function createContract(CreateContractRequest $request)
        {
            $input = $request->validated();
            ......
        }
    

    ps:新的错误验证调用就只有一行了,但因为重写了异常处理,抛出了和以前的一样的异常,所以返回的错误还是和以前一样。

    • 如果不重写failedValidation,就采用新的调用方式,抛出的错误如下:
    {
        "message": "The given data was invalid.",
        "errors": {
            "signer_email": [
                "签订人邮箱字段必须填写"
            ],
            "sign_time": [
                "签订时间字段必须填写"
            ],
        }
    }
    

    ------------------------------------------------ END ----------------------------------------------------------

    相关文章

      网友评论

        本文标题:Laravel 使用 FormRequest 自定义表单验证类,

        本文链接:https://www.haomeiwen.com/subject/fwvgcctx.html