在使用验证器过程中的一些困惑,不知道大家有没有遇到一样的困惑?[最后有贴出个人的解决方式]

问题描述

最近按照文档的说明和给出的示例使用验证器,觉得无论是把验证器代码写在 控制器中,还是根据需要的的功能创建一个表单请求,都觉得很乱。

  • 把验证代码写在控制器中,如果出现需要验证的参数特别多的时候,参数还需要加上规则验证,控制器的代码非常臃肿,而且后期改动起来也非常的麻烦,

    UserController.php 控制器

    /**
    * 创建用户信息
    */
    public function createUserInfo(Request $request, UserService $service)
    {
       //参数验证
       $params = $request->validate([
           'param_a' =>  ['required',bail','max:255'],
           'param_b' =>  ['bail','required'],
           'param_c' =>  ['bail', 'required', 'regex:/^(?=.*[0-9].*)(?=.*[A-Z].*)(?=.*[a-z].*).{8}$/'],
           'param_d' =>  ['bail','required','in:1,2,3'],
           'param_e' =>  ['bail','required'],
           'param_f' =>  ['bail','required'],
           'param_g' =>  ['bail','required'],
           'param_h' =>  ['bail','required'],
       ]);
       //验证成功后逻辑 。。。。
       $service->createUserInfo($params); 
    }
  • 创建一个表单请求,一个控制器方法就得创建一个,当方法越多那创建的验证类也多,比如就一个模块可能有 获取/新增/编辑/删除 每个场景都是验证不同的参数,有的参数在某些场景是必填,有些场景却是选填,这样的话相同模块下得为不同创建创建不同的验证类。

    php artisan make:request CreateUserInfoRequest

    UserController.php 控制器

    /**
    * 创建用户信息
    */
    public function createUserInfo(CreateUserInfoRequest $request,UserService $service)
    {
           //验证参数
        $params = $request->all();
        $service->createUserInfo($params); 
    }

    CreateUserInfoRequest.php 验证器

    /**
     * 获取应用于该请求的验证规则。
     * @return array
     */
    public function rules()
    {
        return [
                'param_a' =>  ['required',bail','max:255'],
                'param_b' =>  ['bail','required'],
                'param_c' =>  ['bail', 'required', 'regex:/^(?=.*[0-9].*)(?=.*[A-Z].*)(?=.*[a-z].*).{8}$/'],
                'param_d' =>  ['bail','required','in:1,2,3'],
                'param_e' =>  ['bail','required'],
                'param_f' =>  ['bail','required'],
                'param_g' =>  ['bail','required'],
                'param_h' =>  ['bail','required'],
        ];
    }

    问题解决

    于是这边就封装了一个 validation 基类, 在基类中修改了 rules 与 messages,使得自动配置控制器名称,这样既解决了 校验代码放在控制器中的臃肿,又解决了因为方法越多,相同模块下需要创建多个 验证类的问题。

    <?php
    /**
     * @Desc: 基类验证器
     */
    namespace App\Validation;
    use Illuminate\Foundation\Http\FormRequest;
    class Validation extends FormRequest
    {
    /**
    * @return \string[][]
    */
    public function rules():array
    {
          $action = $this->getCurrentAction();
          $action = $action ? $action.'Rules' : 'defaultRules';
          return call_user_func_array([$this, $action], []);
    }
    
    /**
     * @return string[]
     */
    public function messages():array
    {
          $action = $this->getCurrentAction();
          $action = $action ? $action.'Messages' : 'defaultMessages';
          return call_user_func_array([$this, $action], []);
    }
    
    /**
     * 获取当前请求的方法
      * @return string
     */
    protected function getCurrentAction():string
    {
          $controller = $this->route()->getAction('controller');
          $action = '';
         if (is_string($controller)){
          $handler = strpos($controller,'@') !== false ? explode('@', $controller) : [];
          $action = !empty($handler) ? end($handler) : $action;
          }
          return $action;
        }
    }

    这样的话,一个控制器只需要定义一个验证器类,控制器中不同的方法需要参数验证,就定义对应方法的验证器方法。

    UserController.php 控制器

    /**
     * 创建用户信息
    */
    public function createUserInfo(UserValidation $validation, UserService $service)
    {
        //参数验证
        $validation->all();
        //逻辑处理
    }
    
    /**
     * 编辑用户信息
    */
    public function editUserInfo(UserValidation $validation, UserService $service)
    {
        //参数验证
        $validation->all();
        //逻辑处理
    }

    UserValidation.php 验证器

    <?php
    namespace App\Validation;
    use App\Validation\Validation;
    class AspNetUsersValidation extends Validation
    {
    /**
     * 创建用户信息验证器
     */
    public function createUserInfoRules(): array
    {
        return [
                'param_b' =>  ['bail','required'],
                'param_c' =>  ['bail', 'required', 'regex:/^(?=.*[0-9].*)(?=.*[A-Z].*)(?=.*[a-z].*).{8}$/'],
                'param_d' =>  ['bail','required','in:1,2,3'],
                'param_e' =>  ['bail','required'],
                'param_f' =>  ['bail','required'],
                'param_g' =>  ['bail','required'],
                'param_h' =>  ['bail','required'],
        ];
    }
    
    /**
     * 编辑用户信息验证器
     */
    public function editUserInfoRules(): array
    {
        return [
                'param_e' =>  ['bail','required'],
                'param_f' =>  ['bail','required'],
                'param_g' =>  ['bail','required'],
                'param_h' =>  ['bail','required'],
        ];
    }
    }
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
最佳答案

我也有这样的困扰,解决方案和你这个很像,但是我是通过路由名字来进行区分的。

<?php
namespace App\Http\Requests;

use Illuminate\Support\Str;

trait RequestValidate
{
    /**
     * 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(): array
    {
        if (method_exists($this, $this->getValidateName())) {
            return call_user_func([$this, $this->getValidateName()]);
        }

        return [];
    }

    /**
     * 动态获取验证方法名称
     *
     * @return string
     */
    protected function getValidateName(): string
    {
        return 'validate' . Str::of(request()->route()->getName())->afterLast('.')->ucfirst();
    }
}
class BankRequest extends FormRequest
{
    use RequestValidate;

    private function validateStore(): array
    {
        return [
            'name' => [
                'required',
                Rule::unique('banks')->where(function ($query) {
                    $query->whereNull('deleted_at');
                }),
            ],
            'weight' => 'required|digits_between:1,99',
            'image' => 'required|image',
        ];
    }

    private function validateUpdate(): array
    {
        return [
            'name' => [
                'required',
                Rule::unique('banks')->ignore($this->route('bank')),
            ],
            'weight' => 'required|digits_between:1,99',
            'image' => 'required_without:old_url|image',
        ];
    }
}
2年前 评论
讨论数量: 17

我也有这样的困扰,解决方案和你这个很像,但是我是通过路由名字来进行区分的。

<?php
namespace App\Http\Requests;

use Illuminate\Support\Str;

trait RequestValidate
{
    /**
     * 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(): array
    {
        if (method_exists($this, $this->getValidateName())) {
            return call_user_func([$this, $this->getValidateName()]);
        }

        return [];
    }

    /**
     * 动态获取验证方法名称
     *
     * @return string
     */
    protected function getValidateName(): string
    {
        return 'validate' . Str::of(request()->route()->getName())->afterLast('.')->ucfirst();
    }
}
class BankRequest extends FormRequest
{
    use RequestValidate;

    private function validateStore(): array
    {
        return [
            'name' => [
                'required',
                Rule::unique('banks')->where(function ($query) {
                    $query->whereNull('deleted_at');
                }),
            ],
            'weight' => 'required|digits_between:1,99',
            'image' => 'required|image',
        ];
    }

    private function validateUpdate(): array
    {
        return [
            'name' => [
                'required',
                Rule::unique('banks')->ignore($this->route('bank')),
            ],
            'weight' => 'required|digits_between:1,99',
            'image' => 'required_without:old_url|image',
        ];
    }
}
2年前 评论

如果你的 CURD 操作请求是根据 Rest 形式写的,可以根据请求类型来区分验证规则

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TestRequest extends FormRequest
{
    public function rules()
    {
        switch($this->method())
        {
            case 'POST':
            {
                return [];
            }
            case 'PUT':
            case 'PATCH':
            {
                return [];
            }
            case 'GET':
            case 'DELETE':
            default:
            {
                return [];
            };
        }
    }
}
2年前 评论
24K大白羊 2年前
Tomo11111 2年前
an_an (楼主) 2年前

thinkphp验证请求有个场景的功能特性,可以参考一下思路

2年前 评论
陈先生

我的建议是一个请求一个 FormRequest

2年前 评论

嗯不错学习下,我用tp 也就感觉他的验证类分了场景这个好用点,其她没啥感觉

2年前 评论
an_an (楼主) 2年前

到最后你还是会发现, 写在控制器里最舒服

2年前 评论
an_an (楼主) 2年前
我爱大可乐 2年前

从一个开源项目里抄来的 其他验证请求继承这个就行,可以定义commonRulesstoreRules

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class Request extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true; // authorization is handled in controllers
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        if (!$this->route()) {
            return [];
        }

        if ($this->route()->getActionMethod() === 'store') {
            return array_merge($this->commonRules(), $this->storeRules());
        }

        if ($this->route()->getActionMethod() === 'batchStore') {
            return $this->buildBatchRules($this->storeRules(), $this->batchStoreRules());
        }

        if ($this->route()->getActionMethod() === 'update') {
            return array_merge($this->commonRules(), $this->updateRules());
        }

        if ($this->route()->getActionMethod() === 'batchUpdate') {
            return $this->buildBatchRules($this->updateRules(), $this->batchUpdateRules());
        }

        if ($this->route()->getActionMethod() === 'associate') {
            return array_merge([
                'related_key' => 'required'
            ], $this->associateRules());
        }

        if ($this->route()->getActionMethod() === 'attach') {
            return array_merge([
                'resources' => 'present',
                'duplicates' => ['sometimes', 'boolean']
            ], $this->attachRules());
        }

        if ($this->route()->getActionMethod() === 'detach') {
            return array_merge([
                'resources' => 'present'
            ], $this->detachRules());
        }

        if ($this->route()->getActionMethod() === 'sync') {
            return array_merge([
                'resources' => 'present',
                'detaching' => ['sometimes', 'boolean']
            ], $this->syncRules());
        }

        if ($this->route()->getActionMethod() === 'toggle') {
            return array_merge([
                'resources' => 'present'
            ], $this->toggleRules());
        }

        if ($this->route()->getActionMethod() === 'updatePivot') {
            return array_merge([
                'pivot' => ['required', 'array']
            ], $this->updatePivotRules());
        }

        return [];
    }

    public function commonRules()
    {
        return [];
    }

    public function storeRules()
    {
        return [];
    }

    protected function buildBatchRules($definedRules, $definedBatchRules): array
    {
        $batchRules = [
            'resources' => ['array', 'required']
        ];

        $mergedRules = array_merge($this->commonRules(), $definedRules, $definedBatchRules);

        foreach ($mergedRules as $ruleField => $fieldRules) {
            $batchRules["resources.*.{$ruleField}"] = $fieldRules;
        }

        return $batchRules;
    }

    public function batchStoreRules()
    {
        return [];
    }

    public function updateRules()
    {
        return [];
    }

    public function batchUpdateRules()
    {
        return [];
    }

    public function associateRules()
    {
        return [];
    }

    public function attachRules()
    {
        return [];
    }

    public function detachRules()
    {
        return [];
    }

    public function syncRules()
    {
        return [];
    }

    public function toggleRules()
    {
        return [];
    }

    public function updatePivotRules()
    {
        return [];
    }
}
2年前 评论
陈先生 2年前
winter-ice (作者) 2年前
陈先生 2年前
orange1994

我都是自己写验证方法,真的讨厌用验证器。

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!