Precognition

Introduction

Laravel Precognition allows you to anticipate the outcome of a future HTTP request. One of the primary use cases of Precognition is the ability to provide "live" validation for your frontend JavaScript application without having to duplicate your application's backend validation rules. Precognition pairs especially well with Laravel's Inertia-based starter kits.

When Laravel receives a "precognitive request", it will execute all of the route's middleware and resolve the route's controller dependencies, including validating form requests - but it will not actually execute the route's controller method.

Live Validation

Using Vue

Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend Vue application. To illustrate how it works, let's build a form for creating new users within our application.

First, to enable Precognition for a route, the HandlePrecognitiveRequests middleware should be added to the route definition. You should also create a form request to house the route's validation rules:

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

Next, you should install the Laravel Precognition frontend helpers for Vue via NPM:

1npm install laravel-precognition-vue
1npm install laravel-precognition-vue

With the Laravel Precognition package installed, you can now create a form object using Precognition's useForm function, providing the HTTP method (post), the target URL (/users), and the initial form data.

Then, to enable live validation, invoke the form's validate method on each input's change event, providing the input's name:

1<script setup>
2import { useForm } from 'laravel-precognition-vue';
3 
4const form = useForm('post', '/users', {
5 name: '',
6 email: '',
7});
8 
9const submit = () => form.submit();
10</script>
11 
12<template>
13 <form @submit.prevent="submit">
14 <label for="name">Name</label>
15 <input
16 id="name"
17 v-model="form.name"
18 @change="form.validate('name')"
19 />
20 <div v-if="form.invalid('name')">
21 {{ form.errors.name }}
22 </div>
23 
24 <label for="email">Email</label>
25 <input
26 id="email"
27 type="email"
28 v-model="form.email"
29 @change="form.validate('email')"
30 />
31 <div v-if="form.invalid('email')">
32 {{ form.errors.email }}
33 </div>
34 
35 <button :disabled="form.processing">
36 Create User
37 </button>
38 </form>
39</template>
1<script setup>
2import { useForm } from 'laravel-precognition-vue';
3 
4const form = useForm('post', '/users', {
5 name: '',
6 email: '',
7});
8 
9const submit = () => form.submit();
10</script>
11 
12<template>
13 <form @submit.prevent="submit">
14 <label for="name">Name</label>
15 <input
16 id="name"
17 v-model="form.name"
18 @change="form.validate('name')"
19 />
20 <div v-if="form.invalid('name')">
21 {{ form.errors.name }}
22 </div>
23 
24 <label for="email">Email</label>
25 <input
26 id="email"
27 type="email"
28 v-model="form.email"
29 @change="form.validate('email')"
30 />
31 <div v-if="form.invalid('email')">
32 {{ form.errors.email }}
33 </div>
34 
35 <button :disabled="form.processing">
36 Create User
37 </button>
38 </form>
39</template>

Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by calling the form's setValidationTimeout function:

1form.setValidationTimeout(3000);
1form.setValidationTimeout(3000);

When a validation request is in-flight, the form's validating property will be true:

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

Any validation errors returned during a validation request or a form submission will automatically populate the form's errors object:

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

You can determine if the form has any errors using the form's hasErrors property:

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

You may also determine if an input has passed or failed validation by passing the input's name to the form's valid and invalid functions, respectively:

1<span v-if="form.valid('email')">
2
3</span>
4 
5<span v-else-if="form.invalid('email')">
6
7</span>
1<span v-if="form.valid('email')">
2
3</span>
4 
5<span v-else-if="form.invalid('email')">
6
7</span>
lightbulb

A form input will only appear as valid or invalid once it has changed and a validation response has been received.

If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's forgetError function to achieve this:

1<input
2 id="avatar"
3 type="file"
4 @change="(e) => {
5 form.avatar = e.target.files[0]
6 
7 form.forgetError('avatar')
8 }"
9>
1<input
2 id="avatar"
3 type="file"
4 @change="(e) => {
5 form.avatar = e.target.files[0]
6 
7 form.forgetError('avatar')
8 }"
9>

As we have seen, you can hook into an input's change event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step.

To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the touch method. Then, call the validate method with onSuccess or onValidationError callbacks:

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

Of course, you may also execute code in reaction to the response to the form submission. The form's submit function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form inputs on successful submission, or handle a failed request:

1const submit = () => form.submit()
2 .then(response => {
3 form.reset();
4 
5 alert('User created.');
6 })
7 .catch(error => {
8 alert('An error occurred.');
9 });
1const submit = () => form.submit()
2 .then(response => {
3 form.reset();
4 
5 alert('User created.');
6 })
7 .catch(error => {
8 alert('An error occurred.');
9 });

You may determine if a form submission request is in-flight by inspecting the form's processing property:

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

Using Vue and Inertia

lightbulb

If you would like a head start when developing your Laravel application with Vue and Inertia, consider using one of our starter kits. Laravel's starter kits provide backend and frontend authentication scaffolding for your new Laravel application.

Before using Precognition with Vue and Inertia, be sure to review our general documentation on using Precognition with Vue. When using Vue with Inertia, you will need to install the Inertia compatible Precognition library via NPM:

1npm install laravel-precognition-vue-inertia
1npm install laravel-precognition-vue-inertia

Once installed, Precognition's useForm function will return an Inertia form helper augmented with the validation features discussed above.

The form helper's submit method has been streamlined, removing the need to specify the HTTP method or URL. Instead, you may pass Inertia's visit options as the first and only argument. In addition, the submit method does not return a Promise as seen in the Vue example above. Instead, you may provide any of Inertia's supported event callbacks in the visit options given to the submit method:

1<script setup>
2import { useForm } from 'laravel-precognition-vue-inertia';
3 
4const form = useForm('post', '/users', {
5 name: '',
6 email: '',
7});
8 
9const submit = () => form.submit({
10 preserveScroll: true,
11 onSuccess: () => form.reset(),
12});
13</script>
1<script setup>
2import { useForm } from 'laravel-precognition-vue-inertia';
3 
4const form = useForm('post', '/users', {
5 name: '',
6 email: '',
7});
8 
9const submit = () => form.submit({
10 preserveScroll: true,
11 onSuccess: () => form.reset(),
12});
13</script>

Using React

Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend React application. To illustrate how it works, let's build a form for creating new users within our application.

First, to enable Precognition for a route, the HandlePrecognitiveRequests middleware should be added to the route definition. You should also create a form request to house the route's validation rules:

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

Next, you should install the Laravel Precognition frontend helpers for React via NPM:

1npm install laravel-precognition-react
1npm install laravel-precognition-react

With the Laravel Precognition package installed, you can now create a form object using Precognition's useForm function, providing the HTTP method (post), the target URL (/users), and the initial form data.

To enable live validation, you should listen to each input's change and blur event. In the change event handler, you should set the form's data with the setData function, passing the input's name and new value. Then, in the blur event handler invoke the form's validate method, providing the input's name:

1import { useForm } from 'laravel-precognition-react';
2 
3export default function Form() {
4 const form = useForm('post', '/users', {
5 name: '',
6 email: '',
7 });
8 
9 const submit = (e) => {
10 e.preventDefault();
11 
12 form.submit();
13 };
14 
15 return (
16 <form onSubmit={submit}>
17 <label htmlFor="name">Name</label>
18 <input
19 id="name"
20 value={form.data.name}
21 onChange={(e) => form.setData('name', e.target.value)}
22 onBlur={() => form.validate('name')}
23 />
24 {form.invalid('name') && <div>{form.errors.name}</div>}
25 
26 <label htmlFor="email">Email</label>
27 <input
28 id="email"
29 value={form.data.email}
30 onChange={(e) => form.setData('email', e.target.value)}
31 onBlur={() => form.validate('email')}
32 />
33 {form.invalid('email') && <div>{form.errors.email}</div>}
34 
35 <button disabled={form.processing}>
36 Create User
37 </button>
38 </form>
39 );
40};
1import { useForm } from 'laravel-precognition-react';
2 
3export default function Form() {
4 const form = useForm('post', '/users', {
5 name: '',
6 email: '',
7 });
8 
9 const submit = (e) => {
10 e.preventDefault();
11 
12 form.submit();
13 };
14 
15 return (
16 <form onSubmit={submit}>
17 <label htmlFor="name">Name</label>
18 <input
19 id="name"
20 value={form.data.name}
21 onChange={(e) => form.setData('name', e.target.value)}
22 onBlur={() => form.validate('name')}
23 />
24 {form.invalid('name') && <div>{form.errors.name}</div>}
25 
26 <label htmlFor="email">Email</label>
27 <input
28 id="email"
29 value={form.data.email}
30 onChange={(e) => form.setData('email', e.target.value)}
31 onBlur={() => form.validate('email')}
32 />
33 {form.invalid('email') && <div>{form.errors.email}</div>}
34 
35 <button disabled={form.processing}>
36 Create User
37 </button>
38 </form>
39 );
40};

Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by calling the form's setValidationTimeout function:

1form.setValidationTimeout(3000);
1form.setValidationTimeout(3000);

When a validation request is in-flight, the form's validating property will be true:

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

Any validation errors returned during a validation request or a form submission will automatically populate the form's errors object:

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

You can determine if the form has any errors using the form's hasErrors property:

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

You may also determine if an input has passed or failed validation by passing the input's name to the form's valid and invalid functions, respectively:

1{form.valid('email') && <span>✅</span>}
2 
3{form.invalid('email') && <span>❌</span>}
1{form.valid('email') && <span>✅</span>}
2 
3{form.invalid('email') && <span>❌</span>}
lightbulb

A form input will only appear as valid or invalid once it has changed and a validation response has been received.

If you are validating a subset of a form's inputs with Precognition, it can be useful to manually clear errors. You may use the form's forgetError function to achieve this:

1<input
2 id="avatar"
3 type="file"
4 onChange={(e) => {
5 form.setData('avatar', e.target.value);
6 
7 form.forgetError('avatar');
8 }}
9>
1<input
2 id="avatar"
3 type="file"
4 onChange={(e) => {
5 form.setData('avatar', e.target.value);
6 
7 form.forgetError('avatar');
8 }}
9>

As we have seen, you can hook into an input's blur event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step.

To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the touch method. Then, call the validate method with onSuccess or onValidationError callbacks:

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

Of course, you may also execute code in reaction to the response to the form submission. The form's submit function returns an Axios request promise. This provides a convenient way to access the response payload, reset the form's inputs on a successful form submission, or handle a failed request:

1const submit = (e) => {
2 e.preventDefault();
3 
4 form.submit()
5 .then(response => {
6 form.reset();
7 
8 alert('User created.');
9 })
10 .catch(error => {
11 alert('An error occurred.');
12 });
13};
1const submit = (e) => {
2 e.preventDefault();
3 
4 form.submit()
5 .then(response => {
6 form.reset();
7 
8 alert('User created.');
9 })
10 .catch(error => {
11 alert('An error occurred.');
12 });
13};

You may determine if a form submission request is in-flight by inspecting the form's processing property:

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

Using React and Inertia

lightbulb

If you would like a head start when developing your Laravel application with React and Inertia, consider using one of our starter kits. Laravel's starter kits provide backend and frontend authentication scaffolding for your new Laravel application.

Before using Precognition with React and Inertia, be sure to review our general documentation on using Precognition with React. When using React with Inertia, you will need to install the Inertia compatible Precognition library via NPM:

1npm install laravel-precognition-react-inertia
1npm install laravel-precognition-react-inertia

Once installed, Precognition's useForm function will return an Inertia form helper augmented with the validation features discussed above.

The form helper's submit method has been streamlined, removing the need to specify the HTTP method or URL. Instead, you may pass Inertia's visit options as the first and only argument. In addition, the submit method does not return a Promise as seen in the React example above. Instead, you may provide any of Inertia's supported event callbacks in the visit options given to the submit method:

1import { useForm } from 'laravel-precognition-react-inertia';
2 
3const form = useForm('post', '/users', {
4 name: '',
5 email: '',
6});
7 
8const submit = (e) => {
9 e.preventDefault();
10 
11 form.submit({
12 preserveScroll: true,
13 onSuccess: () => form.reset(),
14 });
15};
1import { useForm } from 'laravel-precognition-react-inertia';
2 
3const form = useForm('post', '/users', {
4 name: '',
5 email: '',
6});
7 
8const submit = (e) => {
9 e.preventDefault();
10 
11 form.submit({
12 preserveScroll: true,
13 onSuccess: () => form.reset(),
14 });
15};

Using Alpine and Blade

Using Laravel Precognition, you can offer live validation experiences to your users without having to duplicate your validation rules in your frontend Alpine application. To illustrate how it works, let's build a form for creating new users within our application.

First, to enable Precognition for a route, the HandlePrecognitiveRequests middleware should be added to the route definition. You should also create a form request to house the route's validation rules:

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

Next, you should install the Laravel Precognition frontend helpers for Alpine via NPM:

1npm install laravel-precognition-alpine
1npm install laravel-precognition-alpine

Then, register the Precognition plugin with Alpine in your resources/js/app.js file:

1import Alpine from 'alpinejs';
2import Precognition from 'laravel-precognition-alpine';
3 
4window.Alpine = Alpine;
5 
6Alpine.plugin(Precognition);
7Alpine.start();
1import Alpine from 'alpinejs';
2import Precognition from 'laravel-precognition-alpine';
3 
4window.Alpine = Alpine;
5 
6Alpine.plugin(Precognition);
7Alpine.start();

With the Laravel Precognition package installed and registered, you can now create a form object using Precognition's $form "magic", providing the HTTP method (post), the target URL (/users), and the initial form data.

To enable live validation, you should bind the form's data to its relevant input and then listen to each input's change event. In the change event handler, you should invoke the form's validate method, providing the input's name:

1<form x-data="{
2 form: $form('post', '/register', {
3 name: '',
4 email: '',
5 }),
6}">
7 @csrf
8 <label for="name">Name</label>
9 <input
10 id="name"
11 name="name"
12 x-model="form.name"
13 @change="form.validate('name')"
14 />
15 <template x-if="form.invalid('name')">
16 <div x-text="form.errors.name"></div>
17 </template>
18 
19 <label for="email">Email</label>
20 <input
21 id="email"
22 name="email"
23 x-model="form.email"
24 @change="form.validate('email')"
25 />
26 <template x-if="form.invalid('email')">
27 <div x-text="form.errors.email"></div>
28 </template>
29 
30 <button :disabled="form.processing">
31 Create User
32 </button>
33</form>
1<form x-data="{
2 form: $form('post', '/register', {
3 name: '',
4 email: '',
5 }),
6}">
7 @csrf
8 <label for="name">Name</label>
9 <input
10 id="name"
11 name="name"
12 x-model="form.name"
13 @change="form.validate('name')"
14 />
15 <template x-if="form.invalid('name')">
16 <div x-text="form.errors.name"></div>
17 </template>
18 
19 <label for="email">Email</label>
20 <input
21 id="email"
22 name="email"
23 x-model="form.email"
24 @change="form.validate('email')"
25 />
26 <template x-if="form.invalid('email')">
27 <div x-text="form.errors.email"></div>
28 </template>
29 
30 <button :disabled="form.processing">
31 Create User
32 </button>
33</form>

Now, as the form is filled by the user, Precognition will provide live validation output powered by the validation rules in the route's form request. When the form's inputs are changed, a debounced "precognitive" validation request will be sent to your Laravel application. You may configure the debounce timeout by calling the form's setValidationTimeout function:

1form.setValidationTimeout(3000);
1form.setValidationTimeout(3000);

When a validation request is in-flight, the form's validating property will be true:

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

Any validation errors returned during a validation request or a form submission will automatically populate the form's errors object:

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

You can determine if the form has any errors using the form's hasErrors property:

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

You may also determine if an input has passed or failed validation by passing the input's name to the form's valid and invalid functions, respectively:

1<template x-if="form.valid('email')">
2 <span>✅</span>
3</template>
4 
5<template x-if="form.invalid('email')">
6 <span>❌</span>
7</template>
1<template x-if="form.valid('email')">
2 <span>✅</span>
3</template>
4 
5<template x-if="form.invalid('email')">
6 <span>❌</span>
7</template>
lightbulb

A form input will only appear as valid or invalid once it has changed and a validation response has been received.

As we have seen, you can hook into an input's change event and validate individual inputs as the user interacts with them; however, you may need to validate inputs that the user has not yet interacted with. This is common when building a "wizard", where you want to validate all visible inputs, whether the user has interacted with them or not, before moving to the next step.

To do this with Precognition, you should mark the fields you wish to validate as "touched" by passing their names to the touch method. Then, call the validate method with onSuccess or onValidationError callbacks:

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

You may determine if a form submission request is in-flight by inspecting the form's processing property:

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

Repopulating Old Form Data

In the user creation example discussed above, we are using Precognition to perform live validation; however, we are performing a traditional server-side form submission to submit the form. So, the form should be populated with any "old" input and validation errors returned from the server-side form submission:

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

Alternatively, if you would like to submit the form via XHR you may use the form's submit function, which returns an Axios request promise:

1<form
2 x-data="{
3 form: $form('post', '/register', {
4 name: '',
5 email: '',
6 }),
7 submit() {
8 this.form.submit()
9 .then(response => {
10 form.reset();
11 
12 alert('User created.')
13 })
14 .catch(error => {
15 alert('An error occurred.');
16 });
17 },
18 }"
19 @submit.prevent="submit"
20>
1<form
2 x-data="{
3 form: $form('post', '/register', {
4 name: '',
5 email: '',
6 }),
7 submit() {
8 this.form.submit()
9 .then(response => {
10 form.reset();
11 
12 alert('User created.')
13 })
14 .catch(error => {
15 alert('An error occurred.');
16 });
17 },
18 }"
19 @submit.prevent="submit"
20>

Configuring Axios

The Precognition validation libraries use the Axios HTTP client to send requests to your application's backend. For convenience, the Axios instance may be customized if required by your application. For example, when using the laravel-precognition-vue library, you may add additional request headers to each outgoing request in your application's resources/js/app.js file:

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

Or, if you already have a configured Axios instance for your application, you may tell Precognition to use that instance instead:

1import Axios from 'axios';
2import { client } from 'laravel-precognition-vue';
3 
4window.axios = Axios.create()
5window.axios.defaults.headers.common['Authorization'] = authToken;
6 
7client.use(window.axios)
1import Axios from 'axios';
2import { client } from 'laravel-precognition-vue';
3 
4window.axios = Axios.create()
5window.axios.defaults.headers.common['Authorization'] = authToken;
6 
7client.use(window.axios)
lightbulb

The Inertia flavored Precognition libraries will only use the configured Axios instance for validation requests. Form submissions will always be sent by Inertia.

Customizing Validation Rules

It is possible to customize the validation rules executed during a precognitive request by using the request's isPrecognitive method.

For example, on a user creation form, we may want to validate that a password is "uncompromised" only on the final form submission. For precognitive validation requests, we will simply validate that the password is required and has a minimum of 8 characters. Using the isPrecognitive method, we can customize the rules defined by our form request:

1<?php
2 
3namespace App\Http\Requests;
4 
5use Illuminate\Foundation\Http\FormRequest;
6use Illuminate\Validation\Rules\Password;
7 
8class StoreUserRequest extends FormRequest
9{
10 /**
11 * Get the validation rules that apply to the request.
12 *
13 * @return array
14 */
15 protected function rules()
16 {
17 return [
18 'password' => [
19 'required',
20 $this->isPrecognitive()
21 ? Password::min(8)
22 : Password::min(8)->uncompromised(),
23 ],
24 // ...
25 ];
26 }
27}
1<?php
2 
3namespace App\Http\Requests;
4 
5use Illuminate\Foundation\Http\FormRequest;
6use Illuminate\Validation\Rules\Password;
7 
8class StoreUserRequest extends FormRequest
9{
10 /**
11 * Get the validation rules that apply to the request.
12 *
13 * @return array
14 */
15 protected function rules()
16 {
17 return [
18 'password' => [
19 'required',
20 $this->isPrecognitive()
21 ? Password::min(8)
22 : Password::min(8)->uncompromised(),
23 ],
24 // ...
25 ];
26 }
27}

Handling File Uploads

By default, Laravel Precognition does not upload or validate files during a precognitive validation request. This ensure that large files are not unnecessarily uploaded multiple times.

Because of this behavior, you should ensure that your application customizes the corresponding form request's validation rules to specify the field is only required for full form submissions:

1/**
2 * Get the validation rules that apply to the request.
3 *
4 * @return array
5 */
6protected function rules()
7{
8 return [
9 'avatar' => [
10 ...$this->isPrecognitive() ? [] : ['required'],
11 'image',
12 'mimes:jpg,png',
13 'dimensions:ratio=3/2',
14 ],
15 // ...
16 ];
17}
1/**
2 * Get the validation rules that apply to the request.
3 *
4 * @return array
5 */
6protected function rules()
7{
8 return [
9 'avatar' => [
10 ...$this->isPrecognitive() ? [] : ['required'],
11 'image',
12 'mimes:jpg,png',
13 'dimensions:ratio=3/2',
14 ],
15 // ...
16 ];
17}

If you would like to include files in every validation request, you may invoke the validateFiles function on your client-side form instance:

1form.validateFiles();
1form.validateFiles();

Managing Side-Effects

When adding the HandlePrecognitiveRequests middleware to a route, you should consider if there are any side-effects in other middleware that should be skipped during a precognitive request.

For example, you may have a middleware that increments the total number of "interactions" each user has with your application, but you may not want precognitive requests to be counted as an interaction. To accomplish this, we may check the request's isPrecognitive method before incrementing the interaction count:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use App\Facades\Interaction;
6use Closure;
7use Illuminate\Http\Request;
8 
9class InteractionMiddleware
10{
11 /**
12 * Handle an incoming request.
13 */
14 public function handle(Request $request, Closure $next): mixed
15 {
16 if (! $request->isPrecognitive()) {
17 Interaction::incrementFor($request->user());
18 }
19 
20 return $next($request);
21 }
22}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use App\Facades\Interaction;
6use Closure;
7use Illuminate\Http\Request;
8 
9class InteractionMiddleware
10{
11 /**
12 * Handle an incoming request.
13 */
14 public function handle(Request $request, Closure $next): mixed
15 {
16 if (! $request->isPrecognitive()) {
17 Interaction::incrementFor($request->user());
18 }
19 
20 return $next($request);
21 }
22}

Testing

If you would like to make precognitive requests in your tests, Laravel's TestCase includes a withPrecognition helper which will add the Precognition request header.

Additionally, if you would like to assert that a precognitive request was successful, e.g., did not return any validation errors, you may use the assertSuccessfulPrecognition method on the response:

1it('validates registration form with precognition', function () {
2 $response = $this->withPrecognition()
3 ->post('/register', [
4 'name' => 'Taylor Otwell',
5 ]);
6 
7 $response->assertSuccessfulPrecognition();
8 
9 expect(User::count())->toBe(0);
10});
1it('validates registration form with precognition', function () {
2 $response = $this->withPrecognition()
3 ->post('/register', [
4 'name' => 'Taylor Otwell',
5 ]);
6 
7 $response->assertSuccessfulPrecognition();
8 
9 expect(User::count())->toBe(0);
10});
1public function test_it_validates_registration_form_with_precognition()
2{
3 $response = $this->withPrecognition()
4 ->post('/register', [
5 'name' => 'Taylor Otwell',
6 ]);
7 
8 $response->assertSuccessfulPrecognition();
9 $this->assertSame(0, User::count());
10}
1public function test_it_validates_registration_form_with_precognition()
2{
3 $response = $this->withPrecognition()
4 ->post('/register', [
5 'name' => 'Taylor Otwell',
6 ]);
7 
8 $response->assertSuccessfulPrecognition();
9 $this->assertSame(0, User::count());
10}

Comments

No Comments Yet

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.