Precognizione

Introduzione

Laravel Precognition ti permette di anticipare l’esito di una futura richiesta HTTP. Uno degli usi principali di Precognition è la possibilità di fornire una validazione "live" per la tua applicazione frontend JavaScript senza dover duplicare le regole di validazione del backend della tua applicazione. Precognition si integra particolarmente bene con i starter kits basati su Inertia di Laravel.

Quando Laravel riceve una "precognitive request", eseguirà tutta la middleware della rotta e risolverà le dipendenze del controller della rotta, inclusa la validazione delle form requests – ma non eseguirà effettivamente il metodo del controller della rotta.

Validazione in tempo reale

Utilizzo di Vue

Con Laravel Precognition, puoi offrire ai tuoi utenti esperienze di validazione in tempo reale senza dover duplicare le regole di validazione nella tua applicazione frontend Vue. Per mostrare come funziona, creiamo un modulo per la creazione di nuovi utenti nella nostra applicazione.

Per prima cosa, per abilitare Precognition su una rotta, devi aggiungere il middleware HandlePrecognitiveRequests alla definizione della rotta. Devi anche creare una form request per definire le regole di validazione della rotta:

use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;

Route::post('/users', function (StoreUserRequest $request) {
    // ...
})->middleware([HandlePrecognitiveRequests::class]);

Successivamente, installa gli helper frontend di Laravel Precognition per Vue tramite NPM:

npm install laravel-precognition-vue

Con il pacchetto Laravel Precognition installato, puoi creare un oggetto form utilizzando la funzione useForm di Precognition, fornendo il metodo HTTP (post), l’URL di destinazione (/users) e i dati iniziali del form.

Per abilitare la validazione in tempo reale, richiama il metodo validate del form sull’evento change di ogni input, fornendo il nome dell’input:

<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">Nome</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">
            Crea Utente
        </button>
    </form>
</template>

Ora, mentre l’utente compila il modulo, Precognition fornirà un feedback di validazione in tempo reale basato sulle regole di validazione della form request della rotta. Quando gli input del form vengono modificati, verrà inviata una richiesta di validazione "precognitive" con debounce alla tua applicazione Laravel. Puoi configurare il timeout del debounce chiamando la funzione setValidationTimeout del form:

form.setValidationTimeout(3000);

Quando una richiesta di validazione è in corso, la proprietà validating del form sarà true:

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

Qualsiasi errore di validazione restituito durante una richiesta di validazione o una sottomissione del form popolerà automaticamente l’oggetto errors del form:

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

Puoi verificare se il form ha errori utilizzando la proprietà hasErrors del form:

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

Puoi anche determinare se un input ha superato o meno la validazione passando il nome dell’input alle funzioni valid e invalid del form, rispettivamente:

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

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

Un input del form apparirà valido o non valido solo dopo che è stato modificato e ricevuta una risposta di validazione.

Se stai validando un sottoinsieme degli input di un form con Precognition, può essere utile cancellare manualmente gli errori. Puoi usare la funzione forgetError del form per farlo:

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

        form.forgetError('avatar')
    }"
/>

Come abbiamo visto, puoi collegarti all’evento change di un input e validare singoli input mentre l’utente interagisce con essi; tuttavia, potresti dover validare input con cui l’utente non ha ancora interagito. Questo è comune quando si crea un "wizard", dove vuoi validare tutti gli input visibili, indipendentemente dall’interazione dell’utente, prima di passare al passo successivo.

Per fare ciò con Precognition, dovresti segnare i campi che vuoi validare come "toccati" passando i loro nomi al metodo touch. Poi, chiama il metodo validate con callback onSuccess o onValidationError:

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

Naturalmente, puoi anche eseguire del codice in risposta alla sottomissione del form. La funzione submit del form restituisce una promessa di richiesta Axios. Questo fornisce un modo comodo per accedere al payload della risposta, resettare gli input del form in caso di sottomissione riuscita o gestire una richiesta fallita:

const submit = () => form.submit()
    .then(response => {
        form.reset();

        alert('Utente creato.');
    })
    .catch(error => {
        alert('Si è verificato un errore.');
    });

Puoi verificare se una richiesta di sottomissione del form è in corso ispezionando la proprietà processing del form:

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

Utilizzo di Vue e Inertia

Se desideri avere un vantaggio nello sviluppare la tua applicazione Laravel con Vue e Inertia, considera l’uso di uno dei nostri starter kit. Gli starter kit di Laravel forniscono lo scaffolding per l’autenticazione sia backend che frontend per la tua nuova applicazione Laravel.

Prima di utilizzare Precognition con Vue e Inertia, assicurati di consultare la nostra documentazione generale su come utilizzare Precognition con Vue. Quando usi Vue con Inertia, dovrai installare la libreria Precognition compatibile con Inertia tramite NPM:

npm install laravel-precognition-vue-inertia

Una volta installato, la funzione useForm di Precognition restituirà un form helper di Inertia arricchito con le funzionalità di validazione discusse sopra.

Il metodo submit del form helper è stato semplificato, eliminando la necessità di specificare il metodo HTTP o l’URL. Invece, puoi passare le opzioni di visita di Inertia come primo e unico argomento. Inoltre, il metodo submit non restituisce una Promise come visto nell’esempio Vue sopra. Invece, puoi fornire qualsiasi callback di evento supportato da Inertia nelle opzioni di visita passate al metodo 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>

Utilizzo di React

Con Laravel Precognition, puoi offrire esperienze di validazione in tempo reale ai tuoi utenti senza dover duplicare le regole di validazione nella tua applicazione frontend React. Per illustrare come funziona, costruiamo un modulo per creare nuovi utenti all’interno della nostra applicazione.

Innanzitutto, per abilitare Precognition per una rotta, il middleware HandlePrecognitiveRequests deve essere aggiunto alla definizione della rotta. Dovresti anche creare una form request per ospitare le regole di validazione della rotta:

use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;

Route::post('/users', function (StoreUserRequest $request) {
    // ...
})->middleware([HandlePrecognitiveRequests::class]);

Successivamente, dovresti installare gli helper frontend di Laravel Precognition per React tramite NPM:

npm install laravel-precognition-react

Con il pacchetto Laravel Precognition installato, puoi ora creare un oggetto modulo utilizzando la funzione useForm di Precognition, fornendo il metodo HTTP (post), l’URL di destinazione (/users) e i dati iniziali del modulo.

Per abilitare la validazione in tempo reale, devi ascoltare gli eventi change e blur di ogni input. Nel gestore dell’evento change, devi impostare i dati del modulo con la funzione setData, passando il nome dell’input e il nuovo valore. Poi, nel gestore dell’evento blur, invoca il metodo validate del modulo, fornendo il nome dell’input:

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>
    );
};

Ora, mentre l’utente compila il modulo, Precognition fornirà un output di validazione in tempo reale basato sulle regole di validazione nella form request della rotta. Quando gli input del modulo vengono modificati, verrà inviato un request di validazione "precognitiva" con debounce alla tua applicazione Laravel. Puoi configurare il timeout del debounce chiamando la funzione setValidationTimeout del modulo:

form.setValidationTimeout(3000);

Quando una richiesta di validazione è in corso, la proprietà validating del modulo sarà true:

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

Qualsiasi errore di validazione restituito durante una richiesta di validazione o una sottomissione del modulo popolerà automaticamente l’oggetto errors del modulo:

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

Puoi determinare se il modulo ha degli errori utilizzando la proprietà hasErrors del modulo:

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

Puoi anche determinare se un input ha superato o meno la validazione passando il nome dell’input alle funzioni valid e invalid del modulo, rispettivamente:

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

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

Un input del modulo apparirà come valido o non valido solo dopo che è stato modificato e ricevuta una risposta di validazione.

Se stai validando un sottoinsieme degli input di un modulo con Precognition, può essere utile cancellare manualmente gli errori. Puoi usare la funzione forgetError del modulo per farlo:

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

        form.forgetError('avatar');
    }}
>

Come abbiamo visto, puoi agganciare l’evento blur di un input e validare gli input individualmente mentre l’utente interagisce con essi; tuttavia, potresti aver bisogno di validare input con cui l’utente non ha ancora interagito. Questo è comune quando costruisci un "wizard", dove vuoi validare tutti gli input visibili, che l’utente abbia interagito o meno, prima di passare al passo successivo.

Per fare ciò con Precognition, dovresti segnare i campi che desideri validare come "toccati" passando i loro nomi al metodo touch. Poi, chiama il metodo validate con callback onSuccess o onValidationError:

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

Naturalmente, puoi anche eseguire del codice in risposta alla risposta della sottomissione del modulo. La funzione submit del modulo restituisce una promessa di richiesta Axios. Questo fornisce un modo comodo per accedere al payload della risposta, resettare gli input del modulo in caso di sottomissione riuscita, o gestire una richiesta fallita:

const submit = (e) => {
    e.preventDefault();

    form.submit()
        .then(response => {
            form.reset();

            alert('User created.');
        })
        .catch(error => {
            alert('An error occurred.');
        });
};

Puoi determinare se una richiesta di sottomissione del modulo è in corso ispezionando la proprietà processing del modulo:

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

Uso di React e Inertia

Se vuoi iniziare rapidamente a sviluppare la tua applicazione Laravel con React e Inertia, considera di usare uno dei nostri starter kit. Gli starter kit di Laravel forniscono l’autenticazione per il backend e il frontend della tua nuova applicazione Laravel.

Prima di usare Precognition con React e Inertia, assicurati di consultare la nostra documentazione generale su come usare Precognition con React. Quando usi React con Inertia, dovrai installare la libreria Precognition compatibile con Inertia tramite NPM:

npm install laravel-precognition-react-inertia

Una volta installato, la funzione useForm di Precognition restituirà un form helper di Inertia arricchito con le funzionalità di validazione descritte sopra.

Il metodo submit del form helper è stato semplificato, eliminando la necessità di specificare il metodo HTTP o l’URL. Invece, puoi passare le opzioni di visita di Inertia come primo e unico argomento. Inoltre, il metodo submit non restituisce una Promise come visto nell’esempio React sopra. Puoi invece fornire uno qualsiasi dei callback degli eventi supportati da Inertia nelle opzioni di visita passate al metodo 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(),
    });
};

Utilizzo di Alpine e Blade

Con Laravel Precognition, puoi offrire esperienze di validazione in tempo reale ai tuoi utenti senza dover duplicare le regole di validazione nella tua applicazione frontend Alpine. Per capire come funziona, creiamo un modulo per creare nuovi utenti nella nostra applicazione.

Prima, per abilitare Precognition per una rotta, devi aggiungere il middleware HandlePrecognitiveRequests alla definizione della rotta. Dovresti anche creare una form request per contenere le regole di validazione della rotta:

use App\Http\Requests\CreateUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;

Route::post('/users', function (CreateUserRequest $request) {
    // ...
})->middleware([HandlePrecognitiveRequests::class]);

Successivamente, installa i helper frontend di Laravel Precognition per Alpine tramite NPM:

npm install laravel-precognition-alpine

Poi, registra il plugin Precognition con Alpine nel file resources/js/app.js:

import Alpine from 'alpinejs';
import Precognition from 'laravel-precognition-alpine';

window.Alpine = Alpine;

Alpine.plugin(Precognition);
Alpine.start();

Con il pacchetto Laravel Precognition installato e registrato, puoi ora creare un oggetto form utilizzando la "magia" $form di Precognition, fornendo il metodo HTTP (post), l’URL di destinazione (/users) e i dati iniziali del form.

Per abilitare la validazione in tempo reale, devi collegare i dati del form agli input pertinenti e ascoltare l’evento change di ogni input. Nell’handler dell’evento change, devi invocare il metodo validate del form, fornendo il nome dell’input:

<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>

Ora, mentre l’utente compila il form, Precognition fornirà output di validazione in tempo reale basati sulle regole di validazione nella form request della rotta. Quando gli input del form vengono modificati, verrà inviata una richiesta di validazione "precognitive" con debounce alla tua applicazione Laravel. Puoi configurare il timeout del debounce chiamando la funzione setValidationTimeout del form:

form.setValidationTimeout(3000);

Quando una richiesta di validazione è in corso, la proprietà validating del form sarà true:

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

Eventuali errori di validazione restituiti durante una richiesta di validazione o una submission del form popoleranno automaticamente l’oggetto errors del form:

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

Puoi determinare se il form ha errori utilizzando la proprietà hasErrors del form:

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

Puoi anche determinare se un input ha superato o fallito la validazione passando il nome dell’input alle funzioni valid e invalid del form, rispettivamente:

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

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

Un input del form apparirà valido o non valido solo dopo che è stato modificato e ricevuta una risposta di validazione.

Come abbiamo visto, puoi collegarti all’evento change di un input e validare singoli input mentre l’utente interagisce con essi; tuttavia, potresti dover validare input con cui l’utente non ha ancora interagito. Questo è comune quando si crea un "wizard", dove vuoi validare tutti gli input visibili, indipendentemente dall’interazione dell’utente, prima di passare al passaggio successivo.

Per fare ciò con Precognition, devi contrassegnare i campi che desideri validare come "touched" passando i loro nomi al metodo touch. Poi, chiama il metodo validate con callback onSuccess o onValidationError:

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

Puoi determinare se una richiesta di submission del form è in corso ispezionando la proprietà processing del form:

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

Ripopolazione dei Dati Vecchi del Modulo

Nell’esempio di creazione utente discusso sopra, stiamo utilizzando Precognition per eseguire la validazione in tempo reale; tuttavia, stiamo effettuando una tradizionale sottomissione del modulo lato server per inviare il modulo. Pertanto, il modulo dovrebbe essere popolato con eventuali input "vecchi" ed errori di validazione restituiti dalla sottomissione del modulo lato server:

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

In alternativa, se vuoi inviare il modulo tramite XHR, puoi usare la funzione submit del modulo, che restituisce una promessa di richiesta Axios:

<form
    x-data="{
        form: $form('post', '/register', {
            name: '',
            email: '',
        }),
        submit() {
            this.form.submit()
                .then(response => {
                    form.reset();

                    alert('Utente creato.')
                })
                .catch(error => {
                    alert('Si è verificato un errore.');
                });
        },
    }"
    @submit.prevent="submit"
>

Configurazione di Axios

Le librerie di validazione Precognition utilizzano il client HTTP Axios per inviare richieste al backend della tua applicazione. Per comodità, l’istanza di Axios può essere personalizzata se richiesto dalla tua applicazione. Ad esempio, quando si utilizza la libreria laravel-precognition-vue, puoi aggiungere intestazioni di richiesta aggiuntive a ogni richiesta in uscita nel file resources/js/app.js della tua applicazione:

import { client } from 'laravel-precognition-vue';

client.axios().defaults.headers.common['Authorization'] = authToken;

Oppure, se hai già un’istanza di Axios configurata per la tua applicazione, puoi dire a Precognition di utilizzare quell’istanza invece:

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)

[!WARNING]
Le librerie Precognition con flavor Inertia utilizzeranno solo l’istanza di Axios configurata per le richieste di validazione. L’invio dei moduli sarà sempre gestito da Inertia.

Personalizzare le Regole di Validazione

È possibile personalizzare le regole di validazione eseguite durante una richiesta precognitiva utilizzando il metodo isPrecognitive della richiesta.

Ad esempio, in un modulo di creazione utente, potremmo voler validare che una password sia "non compromessa" solo nella sottomissione finale del modulo. Per le richieste di validazione precognitive, verificheremo semplicemente che la password sia richiesta e abbia almeno 8 caratteri. Usando il metodo isPrecognitive, possiamo personalizzare le regole definite nella nostra richiesta di form:

<?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(),
            ],
            // ...
        ];
    }
}

Gestire il Caricamento dei File

Per impostazione predefinita, Laravel Precognition non carica né valida i file durante una richiesta di validazione precognitiva. Questo garantisce che file di grandi dimensioni non vengano caricati più volte inutilmente.

A causa di questo comportamento, dovresti assicurarti che la tua applicazione personalizzi le regole di validazione della richiesta del modulo corrispondente per specificare che il campo è richiesto solo per l’invio completo del modulo:

/**
 * 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',
        ],
        // ...
    ];
}

Se desideri includere i file in ogni richiesta di validazione, puoi invocare la funzione validateFiles sull’istanza del tuo modulo lato client:

form.validateFiles();

Gestire gli Effetti Collaterali

Quando aggiungi il middleware HandlePrecognitiveRequests a una route, dovresti considerare se ci sono effetti collaterali in altri middleware che dovrebbero essere saltati durante una richiesta precognitiva.

Per esempio, potresti avere un middleware che incrementa il numero totale di "interazioni" che ogni utente ha con la tua applicazione, ma potresti non voler che le richieste precognitive vengano conteggiate come un’interazione. Per ottenere ciò, possiamo verificare il metodo isPrecognitive della richiesta prima di incrementare il conteggio delle interazioni:

<?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);
    }
}

Test

Se desideri effettuare richieste precognitive nei tuoi test, la TestCase di Laravel include un helper withPrecognition che aggiunge l’intestazione di richiesta Precognition.

Inoltre, se vuoi verificare che una richiesta precognitiva sia andata a buon fine, ad esempio, che non abbia restituito errori di validazione, puoi utilizzare il metodo assertSuccessfulPrecognition sulla risposta:

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());
}
Lascia un commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *