Precognition
简介
Laravel Precognition 允许您预测未来 HTTP 请求的结果。Precognition 的主要用例之一是能够为您的前端 JavaScript 应用程序提供“实时”验证,而无需复制您的应用程序的后端验证规则。Precognition 与 Laravel 基于 Inertia 的 入门套件 特别搭配。
当 Laravel 接收到“预知请求”时,它将执行所有路由的中间件并解析路由的控制器依赖项,包括验证 表单请求 - 但它实际上不会执行路由的控制器方法。
实时验证
使用 Vue
使用 Laravel Precognition,您可以为用户提供实时验证体验,而无需在前端 Vue 应用程序中复制验证规则。为了说明它的工作原理,让我们在我们的应用程序中构建一个用于创建新用户的表单。
首先,要为路由启用 Precognition,应将 HandlePrecognitiveRequests
中间件添加到路由定义中。您还应该创建一个 表单请求 来存放路由的验证规则
use App\Http\Requests\StoreUserRequest;use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; Route::post('/users', function (StoreUserRequest $request) { // ...})->middleware([HandlePrecognitiveRequests::class]);
接下来,您应该通过 NPM 为 Vue 安装 Laravel Precognition 前端助手
npm install laravel-precognition-vue
安装 Laravel Precognition 包后,您现在可以使用 Precognition 的 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>
现在,当用户填写表单时,Precognition 将提供由路由表单请求中的验证规则支持的实时验证输出。当表单的输入发生更改时,将向您的 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>
您还可以通过将输入的名称传递给表单的 valid
和 invalid
函数来分别确定输入是否通过或失败了验证
<span v-if="form.valid('email')"> ✅</span> <span v-else-if="form.invalid('email')"> ❌</span>
只有在更改并收到验证响应后,表单输入才会显示为有效或无效。
如果您正在使用 Precognition 验证表单输入的一个子集,手动清除错误可能会很有用。您可以使用表单的 forgetError
函数来实现这一点
<input id="avatar" type="file" @change="(e) => { form.avatar = e.target.files[0] form.forgetError('avatar') }">
正如我们所看到的,您可以挂接到输入的 change
事件,并在用户与它们交互时验证单个输入;但是,您可能需要验证用户尚未交互的输入。这在构建“向导”时很常见,您希望在移动到下一步之前验证所有可见的输入,无论用户是否与它们进行了交互。
要使用 Precognition 实现此目的,您应该通过将其名称传递给 touch
方法来将您希望验证的字段标记为“已触摸”。然后,调用带有 onSuccess
或 onValidationError
回调的 validate
方法
<button type="button" @click="form.touch(['name', 'email', 'phone']).validate({ onSuccess: (response) => nextStep(), onValidationError: (response) => /* ... */, })">Next Step</button>
当然,您还可以执行代码来响应表单提交的响应。表单的 submit
函数返回一个 Axios 请求 promise。这提供了一种方便的方式来访问响应负载、在成功提交时重置表单输入或处理失败的请求
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
如果您想在开发带有 Vue 和 Inertia 的 Laravel 应用程序时抢先一步,请考虑使用我们的 入门套件 之一。Laravel 的入门套件为您的新 Laravel 应用程序提供后端和前端身份验证脚手架。
在使用 Precognition 与 Vue 和 Inertia 之前,请务必查看我们关于 将 Precognition 与 Vue 结合使用 的一般文档。当将 Vue 与 Inertia 结合使用时,您需要通过 NPM 安装与 Inertia 兼容的 Precognition 库
npm install laravel-precognition-vue-inertia
安装后,Precognition 的 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 Precognition,您可以为用户提供实时验证体验,而无需在前端 React 应用程序中复制验证规则。为了说明它的工作原理,让我们在我们的应用程序中构建一个用于创建新用户的表单。
首先,要为路由启用 Precognition,应将 HandlePrecognitiveRequests
中间件添加到路由定义中。您还应该创建一个 表单请求 来存放路由的验证规则
use App\Http\Requests\StoreUserRequest;use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; Route::post('/users', function (StoreUserRequest $request) { // ...})->middleware([HandlePrecognitiveRequests::class]);
接下来,您应该通过 NPM 为 React 安装 Laravel Precognition 前端助手
npm install laravel-precognition-react
安装 Laravel Precognition 包后,您现在可以使用 Precognition 的 useForm
函数创建表单对象,提供 HTTP 方法(post
)、目标 URL (/users
) 和初始表单数据。
要启用实时验证,您应该监听每个输入的 change
和 blur
事件。在 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> );};
现在,当用户填写表单时,Precognition 将提供由路由表单请求中的验证规则支持的实时验证输出。当表单的输入发生更改时,将向您的 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>}
您还可以通过将输入的名称传递给表单的 valid
和 invalid
函数来分别确定输入是否通过或失败了验证
{form.valid('email') && <span>✅</span>} {form.invalid('email') && <span>❌</span>}
只有在更改并收到验证响应后,表单输入才会显示为有效或无效。
如果您正在使用 Precognition 验证表单输入的一个子集,手动清除错误可能会很有用。您可以使用表单的 forgetError
函数来实现这一点
<input id="avatar" type="file" onChange={(e) => { form.setData('avatar', e.target.value); form.forgetError('avatar'); }}>
正如我们所见,您可以钩入输入框的 blur
事件,并在用户与输入框交互时验证各个输入;但是,您可能需要验证用户尚未交互的输入。这在构建“向导”时很常见,您需要在移动到下一步之前验证所有可见的输入,无论用户是否与它们交互过。
要使用 Precognition 实现此目的,您应该通过将其名称传递给 touch
方法来将您希望验证的字段标记为“已触摸”。然后,调用带有 onSuccess
或 onValidationError
回调的 validate
方法
<button type="button" onClick={() => form.touch(['name', 'email', 'phone']).validate({ onSuccess: (response) => nextStep(), onValidationError: (response) => /* ... */, })}>Next Step</button>
当然,您也可以执行代码来响应表单提交的响应。表单的 submit
函数返回一个 Axios 请求 Promise。这提供了一种方便的方式来访问响应有效负载、在成功提交表单时重置表单的输入或处理失败的请求。
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
如果您希望在使用 React 和 Inertia 开发 Laravel 应用程序时获得先机,请考虑使用我们的 入门套件 之一。Laravel 的入门套件为您的新 Laravel 应用程序提供后端和前端身份验证脚手架。
在使用 Precognition 与 React 和 Inertia 之前,请务必查看我们关于 使用 Precognition 与 React 的一般文档。当将 React 与 Inertia 一起使用时,您需要通过 NPM 安装与 Inertia 兼容的 Precognition 库。
npm install laravel-precognition-react-inertia
安装后,Precognition 的 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 应用程序中复制您的验证规则。为了说明它是如何工作的,让我们构建一个用于在我们的应用程序中创建新用户的表单。
首先,要为路由启用 Precognition,应将 HandlePrecognitiveRequests
中间件添加到路由定义中。您还应该创建一个 表单请求 来存放路由的验证规则
use App\Http\Requests\CreateUserRequest;use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; Route::post('/users', function (CreateUserRequest $request) { // ...})->middleware([HandlePrecognitiveRequests::class]);
接下来,您应该通过 NPM 为 Alpine 安装 Laravel Precognition 前端助手。
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>
现在,当用户填写表单时,Precognition 将提供由路由表单请求中的验证规则支持的实时验证输出。当表单的输入发生更改时,将向您的 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>
您还可以通过将输入的名称传递给表单的 valid
和 invalid
函数来分别确定输入是否通过或失败了验证
<template x-if="form.valid('email')"> <span>✅</span></template> <template x-if="form.invalid('email')"> <span>❌</span></template>
只有在更改并收到验证响应后,表单输入才会显示为有效或无效。
正如我们所看到的,您可以挂接到输入的 change
事件,并在用户与它们交互时验证单个输入;但是,您可能需要验证用户尚未交互的输入。这在构建“向导”时很常见,您希望在移动到下一步之前验证所有可见的输入,无论用户是否与它们进行了交互。
要使用 Precognition 实现此目的,您应该通过将其名称传递给 touch
方法来将您希望验证的字段标记为“已触摸”。然后,调用带有 onSuccess
或 onValidationError
回调的 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)
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());}