Precognition
Introdução
O Laravel Precognition permite que você antecipe o resultado de uma futura solicitação HTTP. Um dos principais casos de uso do Precognition é a capacidade de fornecer validação "ao vivo" para seu aplicativo JavaScript frontend sem precisar duplicar as regras de validação de backend do seu aplicativo. O Precognition combina especialmente bem com os starter kits baseados em Inertia do Laravel.
Quando o Laravel recebe uma "solicitação precognitiva", ele executará todo o middleware da rota e resolverá as dependências do controlador da rota, incluindo a validação de solicitações de formulário - mas não executará realmente o método do controlador da rota.
Validação ao vivo
Usando o Vue
Usando o Laravel Precognition, você pode oferecer experiências de validação ao vivo para seus usuários sem precisar duplicar suas regras de validação em seu aplicativo Vue frontend. Para ilustrar como funciona, vamos criar um formulário para criar novos usuários em nosso aplicativo.
Primeiro, para habilitar o Precognition para uma rota, o middleware HandlePrecognitiveRequests
deve ser adicionado à definição da rota. Você também deve criar uma solicitação de formulário para abrigar as regras de validação da rota:
use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
Em seguida, você deve instalar os auxiliares de frontend do Laravel Precognition para Vue via NPM:
npm install laravel-precognition-vue
Com o pacote Laravel Precognition instalado, agora você pode criar um objeto de formulário usando a função useForm
do Precognition, fornecendo o método HTTP (post
), a URL de destino (/users
) e os dados iniciais do formulário.
Então, para habilitar a validação ao vivo, invoque o método validate
do formulário em cada evento change
de entrada, fornecendo o nome da entrada:
<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>
Agora, conforme o formulário é preenchido pelo usuário, o Precognition fornecerá uma saída de validação ao vivo alimentada pelas regras de validação na solicitação de formulário da rota. Quando as entradas do formulário forem alteradas, uma solicitação de validação "precognitiva" debounced será enviada ao seu aplicativo Laravel. Você pode configurar o tempo limite de debounce chamando a função setValidationTimeout
do formulário:
form.setValidationTimeout(3000);
Quando uma solicitação de validação estiver em andamento, a propriedade validating
do formulário será true
:
<div v-if="form.validating">
Validating...
</div>
Quaisquer erros de validação retornados durante uma solicitação de validação ou um envio de formulário preencherão automaticamente o objeto errors
do formulário:
<div v-if="form.invalid('email')">
{{ form.errors.email }}
</div>
Você pode determinar se o formulário tem algum erro usando a propriedade hasErrors
do formulário:
<div v-if="form.hasErrors">
<!-- ... -->
</div>
Você também pode determinar se uma entrada passou ou falhou na validação passando o nome da entrada para as funções valid
e invalid
do formulário, respectivamente:
<span v-if="form.valid('email')">
✅
</span>
<span v-else-if="form.invalid('email')">
❌
</span>
AVISO
Uma entrada de formulário só aparecer como válido ou inválido depois que ele for alterado e uma resposta de validação for recebida.
Se você estiver validando um subconjunto de entradas de um formulário com Precognition, pode ser útil limpar erros manualmente. Você pode usar a função forgetError
do formulário para fazer isso:
<input
id="avatar"
type="file"
@change="(e) => {
form.avatar = e.target.files[0]
form.forgetError('avatar')
}"
>
Claro, você também pode executar código em reação à resposta ao envio do formulário. A função submit
do formulário retorna uma promessa de solicitação do Axios. Isso fornece uma maneira conveniente de acessar a carga útil da resposta, redefinir as entradas do formulário em um envio bem-sucedido ou lidar com uma solicitação com falha:
const submit = () => form.submit()
.then(response => {
form.reset();
alert('User created.');
})
.catch(error => {
alert('An error occurred.');
});
Você pode determinar se uma solicitação de envio de formulário está em andamento inspecionando a propriedade processing
do formulário:
<button :disabled="form.processing">
Submit
</button>
Usando Vue e Inertia
NOTA
Se você quiser uma vantagem inicial ao desenvolver seu aplicativo Laravel com Vue e Inertia, considere usar um dos nossos kits iniciais. Os kits iniciais do Laravel fornecem andaimes de autenticação de backend e frontend para seu novo aplicativo Laravel.
Antes de usar o Precognition com Vue e Inertia, certifique-se de revisar nossa documentação geral sobre usando o Precognition com Vue. Ao usar o Vue com Inertia, você precisará instalar a biblioteca Precognition compatível com Inertia via NPM:
npm install laravel-precognition-vue-inertia
Uma vez instalada, a função useForm
do Precognition retornará um form helper do Inertia aumentado com os recursos de validação discutidos acima.
O método submit
do auxiliar de formulário foi simplificado, removendo a necessidade de especificar o método HTTP ou URL. Em vez disso, você pode passar as opções de visita do Inertia como o primeiro e único argumento. Além disso, o método submit
não retorna uma Promise como visto no exemplo do Vue acima. Em vez disso, você pode fornecer qualquer um dos retornos de chamada de evento suportados pelo Inertia nas opções de visita fornecidas ao método submit
:
<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>
Usando React
Usando o Laravel Precognition, você pode oferecer experiências de validação ao vivo para seus usuários sem ter que duplicar suas regras de validação em seu aplicativo React frontend. Para ilustrar como funciona, vamos construir um formulário para criar novos usuários em nosso aplicativo.
Primeiro, para habilitar o Precognition para uma rota, o middleware HandlePrecognitiveRequests
deve ser adicionado à definição da rota. Você também deve criar uma solicitação de formulário para abrigar as regras de validação da rota:
use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
Em seguida, você deve instalar os auxiliares de frontend do Laravel Precognition para React via NPM:
npm install laravel-precognition-react
Com o pacote Laravel Precognition instalado, agora você pode criar um objeto de formulário usando a função useForm
do Precognition, fornecendo o método HTTP (post
), a URL de destino (/users
) e os dados iniciais do formulário.
Para habilitar a validação ao vivo, você deve ouvir os eventos change
e blur
de cada entrada. No manipulador de eventos change
, você deve definir os dados do formulário com a função setData
, passando o nome da entrada e o novo valor. Então, no manipulador de eventos blur
, invoque o método validate
do formulário, fornecendo o nome da entrada:
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 for="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 for="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>
);
};
Agora, conforme o formulário é preenchido pelo usuário, o Precognition fornecerá uma saída de validação ao vivo alimentada pelas regras de validação na solicitação do formulário da rota. Quando as entradas do formulário forem alteradas, uma solicitação de validação "precognitive" debounced será enviada ao seu aplicativo Laravel. Você pode configurar o tempo limite de debounce chamando a função setValidationTimeout
do formulário:
form.setValidationTimeout(3000);
Quando uma solicitação de validação estiver em andamento, a propriedade validating
do formulário será true
:
{form.validating && <div>Validating...</div>}
Quaisquer erros de validação retornados durante uma solicitação de validação ou um envio de formulário preencherão automaticamente o objeto errors
do formulário:
{form.invalid('email') && <div>{form.errors.email}</div>}
Você pode determinar se o formulário tem algum erro usando a propriedade hasErrors
do formulário:
{form.hasErrors && <div><!-- ... --></div>}
Você também pode determinar se uma entrada passou ou falhou na validação passando o nome da entrada para as funções valid
e invalid
do formulário, respectivamente:
{form.valid('email') && <span>✅</span>}
{form.invalid('email') && <span>❌</span>}
AVISO
Uma entrada de formulário só aparecem como válidos ou inválidos depois que são alterados e uma resposta de validação é recebida.
Se você estiver validando um subconjunto de entradas de um formulário com Precognition, pode ser útil limpar erros manualmente. Você pode usar a função forgetError
do formulário para fazer isso:
<input
id="avatar"
type="file"
onChange={(e) =>
form.setData('avatar', e.target.value);
form.forgetError('avatar');
}
>
Claro, você também pode executar código em reação à resposta ao envio do formulário. A função submit
do formulário retorna uma promessa de solicitação do Axios. Isso fornece uma maneira conveniente de acessar a carga útil da resposta, redefinir as entradas do formulário em um envio de formulário bem-sucedido ou lidar com uma solicitação com falha:
const submit = (e) => {
e.preventDefault();
form.submit()
.then(response => {
form.reset();
alert('User created.');
})
.catch(error => {
alert('An error occurred.');
});
};
Você pode determinar se uma solicitação de envio de formulário está em andamento inspecionando a propriedade processing
do formulário:
<button disabled={form.processing}>
Submit
</button>
Usando React e Inertia
NOTA
Se você quiser uma vantagem inicial ao desenvolver seu aplicativo Laravel com React e Inertia, considere usar um dos nossos kits iniciais. Os kits iniciais do Laravel fornecem andaimes de autenticação de backend e frontend para seu novo aplicativo Laravel.
Antes de usar o Precognition com React e Inertia, certifique-se de revisar nossa documentação geral sobre usando o Precognition com React. Ao usar o React com Inertia, você precisará instalar a biblioteca Precognition compatível com Inertia via NPM:
npm install laravel-precognition-react-inertia
Uma vez instalada, a função useForm
do Precognition retornará um form helper do Inertia aumentado com os recursos de validação discutidos acima.
O método submit
do auxiliar de formulário foi simplificado, removendo a necessidade de especificar o método HTTP ou URL. Em vez disso, você pode passar as opções de visita do Inertia como o primeiro e único argumento. Além disso, o método submit
não retorna uma Promise como visto no exemplo React acima. Em vez disso, você pode fornecer qualquer um dos retornos de chamada de evento suportados pelo Inertia nas opções de visita fornecidas ao método submit
:
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(),
});
};
Usando Alpine e Blade
Usando o Laravel Precognition, você pode oferecer experiências de validação ao vivo para seus usuários sem ter que duplicar suas regras de validação em seu aplicativo Alpine frontend. Para ilustrar como funciona, vamos construir um formulário para criar novos usuários em nosso aplicativo.
Primeiro, para habilitar o Precognition para uma rota, o middleware HandlePrecognitiveRequests
deve ser adicionado à definição da rota. Você também deve criar uma solicitação de formulário para abrigar as regras de validação da rota:
use App\Http\Requests\CreateUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (CreateUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
Em seguida, você deve instalar os auxiliares de frontend do Laravel Precognition para Alpine via NPM:
npm install laravel-precognition-alpine
Então, registre o plugin Precognition com Alpine no seu arquivo resources/js/app.js
:
import Alpine from 'alpinejs';
import Precognition from 'laravel-precognition-alpine';
window.Alpine = Alpine;
Alpine.plugin(Precognition);
Alpine.start();
Com o pacote Laravel Precognition instalado e registrado, agora você pode criar um objeto de formulário usando a "mágica" $form
do Precognition, fornecendo o método HTTP (post
), a URL de destino (/users
) e os dados iniciais do formulário.
Para habilitar a validação ao vivo, você deve vincular os dados do formulário à sua entrada relevante e, em seguida, ouvir o evento change
de cada entrada. No manipulador de eventos change
, você deve invocar o método validate
do formulário, fornecendo o nome da entrada:
<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>
Agora, conforme o formulário é preenchido pelo usuário, o Precognition fornecerá uma saída de validação ao vivo alimentada pelas regras de validação na solicitação do formulário da rota. Quando as entradas do formulário forem alteradas, uma solicitação de validação "precognitiva" debounced será enviada ao seu aplicativo Laravel. Você pode configurar o tempo limite de debounce chamando a função setValidationTimeout
do formulário:
form.setValidationTimeout(3000);
Quando uma solicitação de validação estiver em andamento, a propriedade validating
do formulário será true
:
<template x-if="form.validating">
<div>Validating...</div>
</template>
Quaisquer erros de validação retornados durante uma solicitação de validação ou um envio de formulário preencherão automaticamente o objeto errors
do formulário:
<template x-if="form.invalid('email')">
<div x-text="form.errors.email"></div>
</template>
Você pode determinar se o formulário tem algum erro usando a propriedade hasErrors
do formulário:
<template x-if="form.hasErrors">
<div><!-- ... --></div>
</template>
Você também pode determinar se uma entrada passou ou falhou na validação passando o nome da entrada para as funções valid
e invalid
do formulário, respectivamente:
<template x-if="form.valid('email')">
<span>✅</span>
</template>
<template x-if="form.invalid('email')">
<span>❌</span>
</template>
WARNING
Uma entrada de formulário só aparecerá como válida ou inválida depois que for alterada e uma resposta de validação for recebida.
Você pode determinar se uma solicitação de envio de formulário está em andamento inspecionando a propriedade processing
do formulário:
<button :disabled="form.processing">
Submit
</button>
Repopulating Old Form Data
No exemplo de criação de usuário discutido acima, estamos usando o Precognition para executar a validação ao vivo; no entanto, estamos executando um envio de formulário tradicional do lado do servidor para enviar o formulário. Portanto, o formulário deve ser preenchido com qualquer entrada "antiga" e erros de validação retornados do envio do formulário do lado do servidor:
<form x-data="{
form: $form('post', '/register', {
name: '{{ old('name') }}',
email: '{{ old('email') }}',
}).setErrors({{ Js::from($errors->messages()) }}),
}">
Alternativamente, se você quiser enviar o formulário via XHR, pode usar a função submit
do formulário, que retorna uma promessa de solicitação do 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"
>
Configurando o Axios
As bibliotecas de validação do Precognition usam o cliente HTTP Axios para enviar solicitações ao backend do seu aplicativo. Para sua conveniência, a instância do Axios pode ser personalizada se necessário pelo seu aplicativo. Por exemplo, ao usar a biblioteca laravel-precognition-vue
, você pode adicionar cabeçalhos de solicitação adicionais a cada solicitação de saída no arquivo resources/js/app.js
do seu aplicativo:
import { client } from 'laravel-precognition-vue';
client.axios().defaults.headers.common['Authorization'] = authToken;
Ou, se você já tiver uma instância do Axios configurada para seu aplicativo, você pode dizer ao Precognition para usar essa instância:
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)
AVISO
As bibliotecas do Precognition com sabor Inertia usarão apenas a instância configurada do Axios para solicitações de validação. Os envios de formulários sempre serão enviados pelo Inertia.
Personalizando regras de validação
É possível personalizar as regras de validação executadas durante uma solicitação precognitiva usando o método isPrecognitive
da solicitação.
Por exemplo, em um formulário de criação de usuário, podemos querer validar que uma senha é "descomprometida" apenas no envio final do formulário. Para solicitações de validação precognitiva, simplesmente validaremos que a senha é necessária e tem no mínimo 8 caracteres. Usando o método isPrecognitive
, podemos personalizar as regras definidas por nossa solicitação de formulário:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
class StoreUserRequest extends FormRequest
{
/**
* Obtenha as regras de validação que se aplicam à solicitação.
*
* @return array
*/
protected function rules()
{
return [
'password' => [
'required',
$this->isPrecognitive()
? Password::min(8)
: Password::min(8)->uncompromised(),
],
// ...
];
}
}
Lidando com uploads de arquivos
Por padrão, o Laravel Precognition não carrega ou valida arquivos durante uma solicitação de validação precognitiva. Isso garante que arquivos grandes não sejam carregados desnecessariamente várias vezes.
Por causa desse comportamento, você deve garantir que seu aplicativo personalize as regras de validação da solicitação de formulário correspondente para especificar que o campo é necessário apenas para envios de formulário completos:
/**
* Obtenha as regras de validação que se aplicam à solicitação.
*
* @return array
*/
protected function rules()
{
return [
'avatar' => [
...$this->isPrecognitive() ? [] : ['required'],
'image',
'mimes:jpg,png',
'dimensions:ratio=3/2',
],
// ...
];
}
Se você quiser incluir arquivos em cada solicitação de validação, você pode invocar a função validateFiles
na sua instância de formulário do lado do cliente:
form.validateFiles();
Gerenciando efeitos colaterais
Ao adicionar o middleware HandlePrecognitiveRequests
a uma rota, você deve considerar se há algum efeito colateral em outro middleware que deve ser ignorado durante uma solicitação precognitiva.
Por exemplo, você pode ter um middleware que incrementa o número total de "interações" que cada usuário tem com seu aplicativo, mas você pode não querer que solicitações precognitivas sejam contadas como uma interação. Para fazer isso, podemos verificar o método isPrecognitive
da solicitação antes de incrementar a contagem de interações:
<?php
namespace App\Http\Middleware;
use App\Facades\Interaction;
use Closure;
use Illuminate\Http\Request;
class InteractionMiddleware
{
/**
* Lidar com uma solicitação recebida.
*/
public function handle(Request $request, Closure $next): mixed
{
if (! $request->isPrecognitive()) {
Interaction::incrementFor($request->user());
}
return $next($request);
}
}
Testing
Se você quiser fazer solicitações precognitivas em seus testes, o TestCase
do Laravel inclui um auxiliar withPrecognition
que adicionará o cabeçalho de solicitação Precognition
.
Além disso, se você quiser afirmar que uma solicitação precognitiva foi bem-sucedida, por exemplo, não retornou nenhum erro de validação, você pode usar o método assertSuccessfulPrecognition
na resposta:
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());
}