关于Laravel的表单验证分层设计以及验证场景的应用

思考

我发现大多数的Laravel用户会将验证规则写在Controller下,但这会有一个问题,使代码杂乱且无法复用相同的规则。在CRUD为主的项目中,我们会发现好多字段以及它们的验证规则是相同的,我们一般会化身为CV工程师,再复制一份,那么为什么不加一层验证层?

伴随这个问题,然后去看了别的开源项目的代码,发现TP用户的验证会放在一个单独的类中,并且有验证场景来支持他们复用规则,于是我寻找Laravel中有没有对应的解决方案,然后发现了微擎的一个验证器w7/engine-validate,接下里的文章就依赖此扩展来讲如何增加一个验证层

安装

composer require w7/engine-validate

介绍

首先此验证器也是基于illuminate/validation的,关于它的详细说明可以直接查看它的文档,这里就不过多说明了。

使用

首先我们建立一个和控制器相对应的验证层

app
├── Http
│   ├── Controllers
│   │   └── UserController.php
│   └── Validate
│       └── UserValidate.php

验证器

验证器代码:

class UserValidate extends Validate
{
    protected $rule = [
        'user' => 'required|email',
        'pass' => 'required|alpha_num'
    ];

    protected $message = [
        'user.required' => '账号不可为空',
        'pass.required' => '密码不可为空',
    ];

    protected $customAttributes = [
       'user' => '账号',
       'pass' => '密码',
    ];
}

控制器代码:

class UserController extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function login(Request $request)
    {
        $data = UserValidate::make()->check($request->all());
    }
}

这个时候,如果值不符合要求,会抛出一个ValidateException异常,我们可以选择手动捕获,也可以选择在异常捕获类里面做一个全局的处理:

    public function render($request, Throwable $e)
    {
        if ($e instanceof ValidateException) {
            return response()->json([
                'code'    => -1,
                'message' => $e->getMessage()
            ]);
        }

        return parent::render($request, $e);
    }

这样我们就不需要再控制器里面管理任何的验证异常了。

此时,控制器中的$data一定是符合我们需求的数据,然后可以紧接着做对应的业务处理

验证场景

这个时候,如果我们的UserController控制器中又添加了一个register方法,在以上两个字段的需求同时增加了name,应该如何处理呢?

我们可以使用类的$scene属性来指定某一场景下需要验证的字段

验证器代码修改如下:

class UserValidate extends Validate
{
    protected $rule = [
        'user' => 'required|email',
        'pass' => 'required|alpha_num',
        'name' => 'required|alpha'
    ];

    protected $message = [
        'user.required' => '账号不可为空',
        'pass.required' => '密码不可为空',
    ];

    protected $customAttributes = [
       'user' => '账号',
       'pass' => '密码',
       'name' => '用户名称',
    ];

    protected $scene = [
       'login'    => ['user', 'pass'],
       'register' => ['user', 'pass', 'name']
    ];
}

此时:login场景对应的验证userpass字段,而register场景对应的验证user,pass,name字段

控制器代码修改如下:

class UserController extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function login(Request $request)
    {
        $data = UserValidate::make()->scene('login')->check($request->all());
    }

    public function register(Request $request)
    {
        $data = UserValidate::make()->scene('register')->check($request->all());
    }
}

使用验证器的scene方法来指定当前需要验证的场景名称

验证中间件

默认我们一个控制器对应一个验证器,一个方法对应一个场景名称,基于此特点,我们可以编写一些更为简易的方法来解决验证,我们可以编写一个中间件来解决此问题,点击查看文档说明,中间件完整命名空间为:Itwmw\Validate\Middleware\Laravel\ValidateMiddleware

中间件设置

首先我们需要为他指定控制器和验证器的对应关系,在app/Providers目录下新建一个ValidateServiceProvider.php文件,写入如下代码:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Itwmw\Validate\Middleware\ValidateMiddlewareConfig;

class ValidateServiceProvider extends ServiceProvider
{
    public function register()
    {
        ValidateMiddlewareConfig::instance()
            ->setAutoValidatePath('App\\Http\\Controllers\\', 'App\\Http\\Validate\\');
    }
}

其中setAutoValidatePath为指定控制器和验证器的对应关系,可以设定多个,不限制数量,然后我们将ValidateServiceProvider注册一下,在config/app.php文件中找到providers,在其中添加App\Providers\ValidateServiceProvider::class

注意:不可以放在Illuminate\Validation\ValidationServiceProvider::class之前

使用中间件

这个时候,我们可以将中间件注册为全局中间件,也可以不注册,注册方法:中间件《Laravel 7 中文文档》

定义路由:

Route::middleware(ValidateMiddleware::class)->group(function () {
    Route::any('/login', [\App\Http\Controllers\UserController::class, 'login']);
    Route::any('/register', [\App\Http\Controllers\UserController::class, 'register']);
});

控制器代码修改如下:

class UserController extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function login(Request $request)
    {
        $data = get_validate_data($request);
    }

    public function register(Request $request)
    {
        $data = get_validate_data($request);
    }
}

这个时候,我们就不需要手动指定验证器已经场景名了,中间件会自动处理对应的验证规则,我们只需要使用get_validate_data方法来接收值即可。

结尾

此文章只写了这个扩展的一些基本用法,还有更多的功能,需要大家自己看看文档,来完成自己合适的验证层。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 7

表单验证《Laravel 8 中文文档》 你说的这个,和用文档中提到的这个 表单验证器 有啥区别吗?

2年前 评论
Top丶邪少 (楼主) 2年前
kis龍 2年前

弄得确实不错,挺好的!

2年前 评论

好像TP有 scene 的概念

2年前 评论
Top丶邪少 (楼主) 2年前

我也感觉这个挺不错,可能是用tp用的吧 这样去验证真的很方便

2年前 评论

yii的验证有场景的概念挺好用的

2年前 评论

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