關於Laravel的表單驗證分層設計以及驗證場景的應用

Top丶邪少發表於2021-06-18

我發現大多數的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方法來指定當前需要驗證的場景名稱

驗證中介軟體

預設我們一個控制器對應一個驗證器,一個方法對應一個場景名稱,基於此特點,我們可以編寫一些更為簡易的方法來解決驗證,當然此擴充套件也提供了一種解決方案,點選檢視文件說明,擴充套件提供了一箇中介軟體來完成此需求,中介軟體完整名稱空間為:W7\Validate\Support\Middleware\Laravel\ValidateMiddleware

中介軟體設定

首先我們需要為他指定控制器和驗證器的對應關係,在app/Providers目錄下新建一個ValidateServiceProvider.php檔案,寫入如下程式碼:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use W7\Validate\Support\Storage\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 協議》,轉載必須註明作者和本文連結

相關文章