如果要提供給前端作即時表單驗證時,傳統作法是要在提供一組 API 作為驗證返回使用,Laravel Precognition 這個新的 Laravel 套件提供了較為方便的即時驗證,並且不會讓驗證規則同時存在兩份,也與前端框架作了整合,以下將以註冊功能添即時驗證為範例。

前置

配置 Breeze

$ laravel new laravel10
$ cd laravel10
$ composer require laravel/breeze --dev
$ php artisan breeze:install
$ php artisan migrate
$ npm install
$ npm run dev

原本規則

預設配置好之後,原本的規則寫法是送表單型態,也就是說在送出表單當下才會去驗證欄位機制,這邊如果要調整成即時作表單驗證會有兩種作法:

  1. 前端取得後端驗證規則後以 JS 再寫入一次規則
  2. 後端提供驗證 API 操作

這邊可以看到前端寫規則的話當需要作欄位驗證調整時就得再調整,規則分散在兩邊; 而後端自行提供驗證 API 操作需要經過一定設計才會讓代碼不會有重複的狀況。

Laravel Precognition

在先前版本中提供了一種新的工具 Precognition 就是用來解決這個煩惱的。

  1. routes/auth.php 添加中間件作為驗證機制
Route::post('register', [RegisteredUserController::class, 'store'])->middleware([HandlePrecognitiveRequests::class]);
  1. Http/Controllers/Auth/RegisteredUserController.php 將 Validation rule 複製
  2. 創建 RegisterRequest
$ php artisan make:request RegisterRequest
  1. 刪除 authorize 或是改為 true
  2. 添加規則,用於判斷送入與即時驗證 precognitive 要驗證什麼,我這邊僅先用 unique 作範例
return [
    'name' => ['required', 'string', 'max:255'],
    'email' => [
        // 判斷成立 -> 展開陣列到上層陣列
        ...$this->isPrecognitive() ? ['unique:'.User::class] : ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
    ],
    'password' => ['required', 'confirmed', Password::defaults()],
];
  1. 調整 RegisteredUserController.php request 以及 redirect -> response.json,用於後面前端判斷
return response()->json([
    'status' => 0,
    'message' => 'Registered is successfully.'
]);
  1. 調整 Register.vue,引入 pkg 和調用 form
$ npm install laravel-precognition-vue

這邊將原本 inertiajs 調用的 form 改為 precognition,並且針對 submit 後 redirect 進行處理:

import { Head, Link } from '@inertiajs/vue3';
import { useForm } from 'laravel-precognition-vue';

const form = useForm('post', route('register'), {
    name: '',
    email: '',
    password: '',
    password_confirmation: '',
});

const submit = () => {
    form.submit({
        onFinish: () => {
            form.reset('password', 'password_confirmation');
        },
    }).then(response => {
        if (response.status === 200 && response.data.status === 0) {
            window.location.href = '/dashboard';
        }
    }).catch(error => {
        alert(error.response.data.message);
    });
};

//...

<TextInput
    id="email"
    type="email"
    class="mt-1 block w-full"
    v-model="form.email"
    required
    autocomplete="username"
    @change="form.validate('email')" //添加當 value.change 時調用驗證
/>

小結

當邏輯或是一人專案實作時這個組件可說是相當好用且實際,不需要花多餘的時間進行維護兩套規則或是新建 API,但同時還是有他一定的限制存在,例如前後端的套件耦合較高,當然你也可以不透過他的套件操作:

Precognition: true
Precognition-Validate-Only: email

這個套件主要面向應該是個人或小團隊開發者方便調用,或是團隊對於技術流程和程度都很熟悉調用會是非常方便的工具,另外 precognition 還是持續在調整中,如果需要應用於 production 環境可以再等到下一個版本穩定再考慮採用,先了解其流程加入到自己的工具箱內。