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 安装 Laravel Precognition 前端助手以用于 Vue
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 请求承诺。这提供了一种便捷的方法来访问响应有效负载、在提交成功时重置表单输入或处理失败的请求
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 安装 Laravel Precognition 前端助手以用于 React
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 请求承诺。这提供了一种便捷的方法来访问响应有效负载、在表单提交成功时重置表单输入或处理失败的请求
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 的 访问选项 作为第一个且唯一的参数传递。此外,submit
方法不再返回 Promise,如上面的 React 示例所示。相反,您可以在传递给 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 请求承诺
<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());}