跳至內容

預知

簡介

Laravel 預知功能可讓您預期未來 HTTP 請求的結果。預知的主要用途之一是能夠為您的前端 JavaScript 應用程式提供「即時」驗證,而無需重複應用程式的後端驗證規則。預知與 Laravel 基於 Inertia 的 入門套件搭配使用特別好。

當 Laravel 收到「預知請求」時,它將執行路由的所有中介層並解析路由的控制器依賴項,包括驗證 表單請求- 但它實際上不會執行路由的控制器方法。

即時驗證

使用 Vue

使用 Laravel 預知功能,您可以為您的使用者提供即時驗證體驗,而無需在前端 Vue 應用程式中重複您的驗證規則。為了說明它的運作方式,讓我們建置一個表單,用於在我們的應用程式中建立新使用者。

首先,要為路由啟用預知功能,應將 HandlePrecognitiveRequests 中介層新增至路由定義。您還應該建立一個 表單請求,以存放路由的驗證規則

use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
 
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);

接下來,您應該透過 NPM 安裝 Laravel 預知前端輔助程式以用於 Vue

npm install laravel-precognition-vue

安裝 Laravel 預知套件後,您現在可以使用預知的 useForm 函數建立表單物件,提供 HTTP 方法 (post)、目標 URL (/users) 和初始表單資料。

然後,要啟用即時驗證,請在每個輸入的 change 事件上調用表單的 validate 方法,並提供輸入的名稱

<script setup>
import { useForm } from 'laravel-precognition-vue';
 
const form = useForm('post', '/users', {
name: '',
email: '',
});
 
const submit = () => form.submit();
</script>
 
<template>
<form @submit.prevent="submit">
<label for="name">Name</label>
<input
id="name"
v-model="form.name"
@change="form.validate('name')"
/>
<div v-if="form.invalid('name')">
{{ form.errors.name }}
</div>
 
<label for="email">Email</label>
<input
id="email"
type="email"
v-model="form.email"
@change="form.validate('email')"
/>
<div v-if="form.invalid('email')">
{{ form.errors.email }}
</div>
 
<button :disabled="form.processing">
Create User
</button>
</form>
</template>

現在,當使用者填寫表單時,預知功能將提供由路由表單請求中的驗證規則驅動的即時驗證輸出。當表單的輸入變更時,將向您的 Laravel 應用程式傳送一個帶有防抖動的「預知」驗證請求。您可以透過呼叫表單的 setValidationTimeout 函數來設定防抖動逾時時間

form.setValidationTimeout(3000);

當驗證請求正在處理中時,表單的 validating 屬性將為 true

<div v-if="form.validating">
Validating...
</div>

在驗證請求或表單提交期間傳回的任何驗證錯誤都會自動填入表單的 errors 物件

<div v-if="form.invalid('email')">
{{ form.errors.email }}
</div>

您可以使用表單的 hasErrors 屬性來判斷表單是否有任何錯誤

<div v-if="form.hasErrors">
<!-- ... -->
</div>

您也可以分別透過將輸入的名稱傳遞給表單的 validinvalid 函數,來判斷輸入是否通過或未通過驗證

<span v-if="form.valid('email')">
</span>
 
<span v-else-if="form.invalid('email')">
</span>
exclamation

表單輸入只有在變更且收到驗證回應後,才會顯示為有效或無效。

如果您正在使用預知功能驗證表單輸入的子集,則手動清除錯誤會很有用。您可以使用表單的 forgetError 函數來實現此目的

<input
id="avatar"
type="file"
@change="(e) => {
form.avatar = e.target.files[0]
 
form.forgetError('avatar')
}"
>

如我們所見,您可以掛鉤到輸入的 change 事件,並在使用者與輸入互動時驗證個別輸入;但是,您可能需要驗證使用者尚未互動的輸入。在建置「精靈」時,這種情況很常見,在「精靈」中,您想要在移至下一步之前驗證所有可見的輸入,無論使用者是否與它們互動。

若要使用預知功能執行此操作,您應該將您想要驗證的欄位標記為「已接觸」,方法是將其名稱傳遞至 touch 方法。然後,使用 onSuccessonValidationError 回呼呼叫 validate 方法

<button
type="button"
@click="form.touch(['name', 'email', 'phone']).validate({
onSuccess: (response) => nextStep(),
onValidationError: (response) => /* ... */,
})"
>Next Step</button>

當然,您也可以執行程式碼以回應表單提交的回應。表單的 submit 函數會傳回 Axios 請求承諾。這提供了一種方便的方法來存取回應有效負載、在成功提交時重設表單輸入,或處理失敗的請求

const submit = () => form.submit()
.then(response => {
form.reset();
 
alert('User created.');
})
.catch(error => {
alert('An error occurred.');
});

您可以透過檢查表單的 processing 屬性,來判斷表單提交請求是否正在處理中

<button :disabled="form.processing">
Submit
</button>

使用 Vue 和 Inertia

lightbulb

如果您想在透過 Vue 和 Inertia 開發 Laravel 應用程式時有一個好的開始,請考慮使用我們的 入門套件。Laravel 的入門套件為您的新 Laravel 應用程式提供後端和前端驗證框架。

在將預知功能與 Vue 和 Inertia 搭配使用之前,請務必檢閱我們關於 將預知功能與 Vue 搭配使用的一般文件。將 Vue 與 Inertia 搭配使用時,您需要透過 NPM 安裝與 Inertia 相容的預知程式庫

npm install laravel-precognition-vue-inertia

安裝後,預知的 useForm 函數將傳回一個 Inertia 表單輔助程式,其中增強了上面討論的驗證功能。

表單輔助程式的 submit 方法已簡化,無需指定 HTTP 方法或 URL。相反地,您可以將 Inertia 的 訪問選項作為第一個也是唯一的引數傳遞。此外,submit 方法不會像上面的 Vue 範例中那樣傳回 Promise。相反地,您可以在提供給 submit 方法的訪問選項中,提供 Inertia 的任何支援的 事件回呼

<script setup>
import { useForm } from 'laravel-precognition-vue-inertia';
 
const form = useForm('post', '/users', {
name: '',
email: '',
});
 
const submit = () => form.submit({
preserveScroll: true,
onSuccess: () => form.reset(),
});
</script>

使用 React

使用 Laravel 預知功能,您可以為您的使用者提供即時驗證體驗,而無需在前端 React 應用程式中重複您的驗證規則。為了說明它的運作方式,讓我們建置一個表單,用於在我們的應用程式中建立新使用者。

首先,要為路由啟用預知功能,應將 HandlePrecognitiveRequests 中介層新增至路由定義。您還應該建立一個 表單請求,以存放路由的驗證規則

use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
 
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);

接下來,您應該透過 NPM 安裝 Laravel 預知前端輔助程式以用於 React

npm install laravel-precognition-react

安裝 Laravel 預知套件後,您現在可以使用預知的 useForm 函數建立表單物件,提供 HTTP 方法 (post)、目標 URL (/users) 和初始表單資料。

若要啟用即時驗證,您應該監聽每個輸入的 changeblur 事件。在 change 事件處理常式中,您應該使用 setData 函數設定表單的資料,傳遞輸入的名稱和新值。然後,在 blur 事件處理常式中,調用表單的 validate 方法,並提供輸入的名稱

import { useForm } from 'laravel-precognition-react';
 
export default function Form() {
const form = useForm('post', '/users', {
name: '',
email: '',
});
 
const submit = (e) => {
e.preventDefault();
 
form.submit();
};
 
return (
<form onSubmit={submit}>
<label htmlFor="name">Name</label>
<input
id="name"
value={form.data.name}
onChange={(e) => form.setData('name', e.target.value)}
onBlur={() => form.validate('name')}
/>
{form.invalid('name') && <div>{form.errors.name}</div>}
 
<label htmlFor="email">Email</label>
<input
id="email"
value={form.data.email}
onChange={(e) => form.setData('email', e.target.value)}
onBlur={() => form.validate('email')}
/>
{form.invalid('email') && <div>{form.errors.email}</div>}
 
<button disabled={form.processing}>
Create User
</button>
</form>
);
};

現在,當使用者填寫表單時,預知功能將提供由路由表單請求中的驗證規則驅動的即時驗證輸出。當表單的輸入變更時,將向您的 Laravel 應用程式傳送一個帶有防抖動的「預知」驗證請求。您可以透過呼叫表單的 setValidationTimeout 函數來設定防抖動逾時時間

form.setValidationTimeout(3000);

當驗證請求正在處理中時,表單的 validating 屬性將為 true

{form.validating && <div>Validating...</div>}

在驗證請求或表單提交期間傳回的任何驗證錯誤都會自動填入表單的 errors 物件

{form.invalid('email') && <div>{form.errors.email}</div>}

您可以使用表單的 hasErrors 屬性來判斷表單是否有任何錯誤

{form.hasErrors && <div><!-- ... --></div>}

您也可以分別透過將輸入的名稱傳遞給表單的 validinvalid 函數,來判斷輸入是否通過或未通過驗證

{form.valid('email') && <span></span>}
 
{form.invalid('email') && <span></span>}
exclamation

表單輸入只有在變更且收到驗證回應後,才會顯示為有效或無效。

如果您正在使用預知功能驗證表單輸入的子集,則手動清除錯誤會很有用。您可以使用表單的 forgetError 函數來實現此目的

<input
id="avatar"
type="file"
onChange={(e) => {
form.setData('avatar', e.target.value);
 
form.forgetError('avatar');
}}
>

如我們所見,您可以掛鉤到輸入的 blur 事件,並在使用者與輸入互動時驗證個別輸入;但是,您可能需要驗證使用者尚未互動的輸入。在建置「精靈」時,這種情況很常見,在「精靈」中,您想要在移至下一步之前驗證所有可見的輸入,無論使用者是否與它們互動。

若要使用預知功能執行此操作,您應該將您想要驗證的欄位標記為「已接觸」,方法是將其名稱傳遞至 touch 方法。然後,使用 onSuccessonValidationError 回呼呼叫 validate 方法

<button
type="button"
onClick={() => form.touch(['name', 'email', 'phone']).validate({
onSuccess: (response) => nextStep(),
onValidationError: (response) => /* ... */,
})}
>Next Step</button>

當然,您也可以執行程式碼以回應表單提交的回應。表單的 submit 函數會傳回 Axios 請求承諾。這提供了一種方便的方法來存取回應有效負載、在成功提交表單時重設表單的輸入,或處理失敗的請求

const submit = (e) => {
e.preventDefault();
 
form.submit()
.then(response => {
form.reset();
 
alert('User created.');
})
.catch(error => {
alert('An error occurred.');
});
};

您可以透過檢查表單的 processing 屬性,來判斷表單提交請求是否正在處理中

<button disabled={form.processing}>
Submit
</button>

使用 React 和 Inertia

lightbulb

如果您想在開發使用 React 和 Inertia 的 Laravel 應用程式時搶先一步,請考慮使用我們的入門套件之一。Laravel 的入門套件為您的新 Laravel 應用程式提供後端和前端身份驗證的骨架。

在將 Precognition 與 React 和 Inertia 搭配使用之前,請務必查看我們關於將 Precognition 與 React 搭配使用的一般文件。當將 React 與 Inertia 搭配使用時,您需要透過 NPM 安裝與 Inertia 相容的 Precognition 函式庫。

npm install laravel-precognition-react-inertia

安裝後,預知的 useForm 函數將傳回一個 Inertia 表單輔助程式,其中增強了上面討論的驗證功能。

表單輔助工具的 submit 方法已精簡,不再需要指定 HTTP 方法或 URL。相反地,您可以將 Inertia 的訪問選項作為第一個也是唯一的參數傳遞。此外,如上面的 React 範例所示,submit 方法不會返回 Promise。相反地,您可以在傳遞給 submit 方法的訪問選項中提供任何 Inertia 支援的事件回呼

import { useForm } from 'laravel-precognition-react-inertia';
 
const form = useForm('post', '/users', {
name: '',
email: '',
});
 
const submit = (e) => {
e.preventDefault();
 
form.submit({
preserveScroll: true,
onSuccess: () => form.reset(),
});
};

使用 Alpine 和 Blade

使用 Laravel Precognition,您可以為您的使用者提供即時驗證體驗,而無需在您的前端 Alpine 應用程式中重複您的驗證規則。為了說明其運作方式,讓我們建立一個在我們的應用程式中建立新使用者的表單。

首先,要為路由啟用預知功能,應將 HandlePrecognitiveRequests 中介層新增至路由定義。您還應該建立一個 表單請求,以存放路由的驗證規則

use App\Http\Requests\CreateUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
 
Route::post('/users', function (CreateUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);

接下來,您應該透過 NPM 安裝 Laravel Precognition 的 Alpine 前端輔助工具。

npm install laravel-precognition-alpine

然後,在您的 resources/js/app.js 檔案中向 Alpine 註冊 Precognition 外掛程式。

import Alpine from 'alpinejs';
import Precognition from 'laravel-precognition-alpine';
 
window.Alpine = Alpine;
 
Alpine.plugin(Precognition);
Alpine.start();

在安裝並註冊 Laravel Precognition 套件後,您現在可以使用 Precognition 的 $form「魔法」建立表單物件,提供 HTTP 方法 (post)、目標 URL (/users) 和初始表單資料。

要啟用即時驗證,您應該將表單的資料繫結到其相關的輸入,然後監聽每個輸入的 change 事件。在 change 事件處理常式中,您應該調用表單的 validate 方法,提供輸入的名稱。

<form x-data="{
form: $form('post', '/register', {
name: '',
email: '',
}),
}">
@csrf
<label for="name">Name</label>
<input
id="name"
name="name"
x-model="form.name"
@change="form.validate('name')"
/>
<template x-if="form.invalid('name')">
<div x-text="form.errors.name"></div>
</template>
 
<label for="email">Email</label>
<input
id="email"
name="email"
x-model="form.email"
@change="form.validate('email')"
/>
<template x-if="form.invalid('email')">
<div x-text="form.errors.email"></div>
</template>
 
<button :disabled="form.processing">
Create User
</button>
</form>

現在,當使用者填寫表單時,預知功能將提供由路由表單請求中的驗證規則驅動的即時驗證輸出。當表單的輸入變更時,將向您的 Laravel 應用程式傳送一個帶有防抖動的「預知」驗證請求。您可以透過呼叫表單的 setValidationTimeout 函數來設定防抖動逾時時間

form.setValidationTimeout(3000);

當驗證請求正在處理中時,表單的 validating 屬性將為 true

<template x-if="form.validating">
<div>Validating...</div>
</template>

在驗證請求或表單提交期間傳回的任何驗證錯誤都會自動填入表單的 errors 物件

<template x-if="form.invalid('email')">
<div x-text="form.errors.email"></div>
</template>

您可以使用表單的 hasErrors 屬性來判斷表單是否有任何錯誤

<template x-if="form.hasErrors">
<div><!-- ... --></div>
</template>

您也可以分別透過將輸入的名稱傳遞給表單的 validinvalid 函數,來判斷輸入是否通過或未通過驗證

<template x-if="form.valid('email')">
<span></span>
</template>
 
<template x-if="form.invalid('email')">
<span></span>
</template>
exclamation

表單輸入只有在變更且收到驗證回應後,才會顯示為有效或無效。

如我們所見,您可以掛鉤到輸入的 change 事件,並在使用者與輸入互動時驗證個別輸入;但是,您可能需要驗證使用者尚未互動的輸入。在建置「精靈」時,這種情況很常見,在「精靈」中,您想要在移至下一步之前驗證所有可見的輸入,無論使用者是否與它們互動。

若要使用預知功能執行此操作,您應該將您想要驗證的欄位標記為「已接觸」,方法是將其名稱傳遞至 touch 方法。然後,使用 onSuccessonValidationError 回呼呼叫 validate 方法

<button
type="button"
@change="form.touch(['name', 'email', 'phone']).validate({
onSuccess: (response) => nextStep(),
onValidationError: (response) => /* ... */,
})"
>Next Step</button>

您可以透過檢查表單的 processing 屬性,來判斷表單提交請求是否正在處理中

<button :disabled="form.processing">
Submit
</button>

重新填入舊表單資料

在上面討論的使用者建立範例中,我們使用 Precognition 執行即時驗證;但是,我們正在執行傳統的伺服器端表單提交來提交表單。因此,表單應該填入伺服器端表單提交返回的任何「舊」輸入和驗證錯誤。

<form x-data="{
form: $form('post', '/register', {
name: '{{ old('name') }}',
email: '{{ old('email') }}',
}).setErrors({{ Js::from($errors->messages()) }}),
}">

或者,如果您想透過 XHR 提交表單,您可以使用表單的 submit 函數,該函數會返回 Axios 請求 Promise。

<form
x-data="{
form: $form('post', '/register', {
name: '',
email: '',
}),
submit() {
this.form.submit()
.then(response => {
form.reset();
 
alert('User created.')
})
.catch(error => {
alert('An error occurred.');
});
},
}"
@submit.prevent="submit"
>

設定 Axios

Precognition 驗證函式庫使用 Axios HTTP 客戶端將請求傳送到您的應用程式後端。為了方便起見,如果您的應用程式需要,可以自訂 Axios 實例。例如,當使用 laravel-precognition-vue 函式庫時,您可以在應用程式的 resources/js/app.js 檔案中為每個外送請求新增額外的請求標頭。

import { client } from 'laravel-precognition-vue';
 
client.axios().defaults.headers.common['Authorization'] = authToken;

或者,如果您的應用程式已經有已設定的 Axios 實例,您可以告知 Precognition 改用該實例。

import Axios from 'axios';
import { client } from 'laravel-precognition-vue';
 
window.axios = Axios.create()
window.axios.defaults.headers.common['Authorization'] = authToken;
 
client.use(window.axios)
exclamation

Inertia 風味的 Precognition 函式庫只會將已設定的 Axios 實例用於驗證請求。表單提交將始終由 Inertia 發送。

自訂驗證規則

可以透過使用請求的 isPrecognitive 方法來自訂在預知請求期間執行的驗證規則。

例如,在使用者建立表單上,我們可能只想在最終表單提交時驗證密碼是否為「未洩露」。對於預知驗證請求,我們只會驗證密碼是否為必填且至少有 8 個字元。使用 isPrecognitive 方法,我們可以自訂表單請求定義的規則。

<?php
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
 
class StoreUserRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
protected function rules()
{
return [
'password' => [
'required',
$this->isPrecognitive()
? Password::min(8)
: Password::min(8)->uncompromised(),
],
// ...
];
}
}

處理檔案上傳

預設情況下,Laravel Precognition 在預知驗證請求期間不會上傳或驗證檔案。這確保了不會多次不必要地上傳大型檔案。

由於此行為,您應該確保您的應用程式自訂相應的表單請求的驗證規則,以指定該欄位僅在完整表單提交時才是必填。

/**
* Get the validation rules that apply to the request.
*
* @return array
*/
protected function rules()
{
return [
'avatar' => [
...$this->isPrecognitive() ? [] : ['required'],
'image',
'mimes:jpg,png',
'dimensions:ratio=3/2',
],
// ...
];
}

如果您想在每個驗證請求中包含檔案,您可以調用客戶端表單實例上的 validateFiles 函數。

form.validateFiles();

管理副作用

HandlePrecognitiveRequests 中介軟體新增至路由時,您應該考慮在預知請求期間是否應該跳過其他中介軟體中的任何副作用。

例如,您可能有一個中介軟體會遞增每個使用者與您的應用程式互動的總數,但您可能不希望將預知請求視為互動。為了實現此目的,我們可以在遞增互動計數之前檢查請求的 isPrecognitive 方法。

<?php
 
namespace App\Http\Middleware;
 
use App\Facades\Interaction;
use Closure;
use Illuminate\Http\Request;
 
class InteractionMiddleware
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): mixed
{
if (! $request->isPrecognitive()) {
Interaction::incrementFor($request->user());
}
 
return $next($request);
}
}

測試

如果您想在測試中進行預知請求,Laravel 的 TestCase 包含一個 withPrecognition 輔助工具,它會新增 Precognition 請求標頭。

此外,如果您想斷言預知請求成功(例如,沒有返回任何驗證錯誤),您可以在回應上使用 assertSuccessfulPrecognition 方法。

it('validates registration form with precognition', function () {
$response = $this->withPrecognition()
->post('/register', [
'name' => 'Taylor Otwell',
]);
 
$response->assertSuccessfulPrecognition();
 
expect(User::count())->toBe(0);
});
public function test_it_validates_registration_form_with_precognition()
{
$response = $this->withPrecognition()
->post('/register', [
'name' => 'Taylor Otwell',
]);
 
$response->assertSuccessfulPrecognition();
$this->assertSame(0, User::count());
}