Laravel e la validazione di un form


laravel-e-la-validazione-di-un-form

Una delle situazioni più classiche in cui puoi imbatterti, è quella di dover realizzare un form di raccolta dati con relativo sistema di validazione dei dati inseriti dall'utente.
Laravel fornisce una serie di strumenti e metodologie che ti semplificano la vita nella realizzazione di questo meccanismo.

Lo scenario

Per questo tutorial, ho ipotizzato questo scenario:

  • un utente compila i dati di un form per lasciare i propri dati;
  • riceve informazioni su eventuali dati non congruenti con le regole di validazione;
  • riceve un messaggio di conferma se tutto è corretto;
  • i dati vengono salvati nel database in maniera permanente.

I componenti di cui hai bisogno sono, perciò:

  • una view con il form ( contatti.blade.php )
  • una view con la conferma dell’invio ( conferma.blade.php )
  • un controller con 3 metodi ( ContattiController.php )
  • un model (Contatto.php)

Devi inoltre aggiungere al file route.php, le url necessarie e, infine, per creare la tabella nel database, puoi usare lo strumento delle migration, definendo la sua struttura nel file create_table_contatti.php.

Le nuove route

Il file app/Http/routes.php è il crocevia che contiene tutte le direzioni possili del nostro sito o della nostra applicazione.
E' lì che devi inserire le url che ti servono per raggiungere i tre metodi del controller.

Route::group(['middleware' => ['web']], function () {
	// le tue routes qui
});

Route::group permette di riunire al suo interno tutta una serie di route che hanno qualcosa in comune: in questo caso il middleware web.
Tra le altre cose, questo middleware mette a disposizione l'oggetto MessageBag che contiente gli errori dell'eventuale mancata validazione da visualizzare nella view.

Per cui inseriamo le route che ci servono:

Route::get('contatti', 'ContattiController@formContatti');
Route::post('contatti', 'ContattiController@inviaDati');
Route::get('conferma', 'ContattiController@conferma');

Puoi notare alcune particolarità:

  • per ogni route puoi indicare il metodo con cui sono inviati i dati, get o post ( i metodi possono anche altri: put, patch, delete e la wildcard any);
  • ho lasciato volontariamente lo stesso nome per due routes diverse, per farti vedere che due routes con lo stesso nome, ma con due metodi differenti, possono puntare a due funzioni differenti;
  • come secondo parametro puoi passare una funzione, oppure, come ho fatto qui, il riferimento al metodo del controller con il codice relativo, nella forma NomeController@nomeMetodo.

La migration

Non sto qui a dilungarmi sulle migrations: mi limito a dire che sono uno strumento che ti permette di gestire il database, tramite linea di comando, per crearlo, ricrearlo, annullare gli ultimi cambiamenti e altre cose molto, molto utili. E’ uno di quegli strumenti che, se non conosci, una volta scoperto, non potrai più farne a meno.

Tramite comando artisan, perciò, crea una nuova migration per la tabella contatti:

artisan make:migration create_table_contatti —create=contatti

Artisan si occupa di creare un nuovo file nella cartella database/migrations; il prefisso numerico, che aggiunge, indica il momento in cui hai creato il file ( anno_mese_giorno_oraminutisecondi).

Perché questo prefisso?
Per dare un ordine alla creazione delle migrations o alla loro cancellazione.

Aprendo il file troverai due funzioni:

  • la funzione up(), in cui scriverai lo schema della tabella;
  • la funzione down(), in cui scriverai il codice per eliminare la tabella nelle procedure di rollback o di reset del database.

Inserendo l’opzione —create=contatti, nel comando artisan, hai detto a Laravel come si deve chiamare la tabella, contatti appunto. Laravel ha già, quindi, inserito al suo interno il nome, ma anche due righe che servono a:

  • $table->increments('id') : creare una colonna id, autoincrement, primary e unsigned;
  • $table->timestamps() : creare due colonne (created_at e updated_at) che servono per salvare i timestamp di inserimento e di modifica del record.

Ottimo! Metà del lavoro è fatto.

Ora, tra le due righe, inserisci la definizione delle altre colonne, 3 colonne di tipo string e una di tipo boolean che conterranno, nell'ordine:

  • il nome dell’utente,
  • il nome
  • la mail
  • la preferenza dell’utente per la ricezione delle comunicazioni (si o no)

Il metodo up() dovrà risultare come segue:

public function up() {
    Schema::create('contatti', function (Blueprint $table) {
        $table->increments('id');
        $table->string('nome');
        $table->string('cognome');
        $table->string('email');
        $table->boolean(‘conferma_messaggi’)->default(false);
        $table->timestamps();
    });
};

Le migration usano la sintassi di Laravel per definire le strutture delle tabelle indipendentemente dal DB che userai.
Il tuo sito avrà come DB PostgreSQL? La migrazione che hai appena creato andrà bene! Vuoi passare a SQLite? Nessun problema.
Indica solo le credenziali della tua base di dati nel file .env e lanciando la migrazione, sarai in grado di creare le tabelle.

Nel campo boolean, per l’accettazione o meno dell’invio delle comunicazioni, ho specificato che di default l’utente non accetta, così, se il checkbox non viene selezionato, avrò comunque un valore di default nella tabella.
Un’altra buona notizia: il metodo down è già stato compilato per noi da Laravel: contiene l’indicazione per il drop della tabella contatti.

Ora che abbiamo scritto tutto il codice necessario, lancia la migration digitando:

artisan migrate

Viene creata la tabella contatti insieme ad un’altra tabella, migrations che serve a Laravel per tenere traccia dell’ordine con cui sono state realizzate le migrazioni.

La view del form

La view si trova in resources/views/contatti.blade.php ed è molto semplice: le classi css che vedi nel codice sono quelle del framework bootstrap.
Il form funziona indipendentemente dal fatto che tu scarichi il framework css o meno.

Il codice risultante è questo:

<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h1>Lascia i tuoi contatti:</h1>
        </div>
    </div>
    <br>
    <div class="row">
        <div class="col-md-12">
            <form action="{{ url(‘invia-dati') }}" method="POST">
                <input type="hidden" name="_token" value="{{ csrf_token() }}">
                <div class="form-group">
                    <label for="nome">Nome</label>
                    <input type="text" name="nome" class="form-control">
                </div>
                <div class="form-group">
                    <label for="cognome">Cognome</label>
                    <input type="text" name="cognome" class="form-control">
                </div>
                <div class="form-group">
                    <label for="email">Email</label>
                    <input type="text" name="email" class="form-control">
                </div>
                <div class="checkbox">
                    <label>
                        <input type="checkbox" name="conferma_messaggi"> Spunta questo riquadro se vuoi ricevere messaggi
                    </label>
                </div>
                <div class="text-center">
                    <input type="submit" value="invia" class="btn btn-primary btn-block">
                </div>
            </form>
        </div>
    </div>
</div>

Ti dico due cose:

  • form action="{{ url(‘invia-dati') }}” è l’url a cui manderemo i dati del form quando faremo il submit;
  • <input type="hidden" name="_token" value="{{ csrf_token() }}”> è un campo nascosto in cui Laravel, tramite l'helper csrf_token(), inserisce una stringa alfanumerica per evitare il CSRF (Cross Site Request Forgery - qui puoi approfondire l’argomento ). 
Ho usato la funzione che stampa solo il token, ma Laravel ti mette a disposizione anche l'helper che stampa l’intero hidden input: {!! csrf_field() !!}

La view, per il momento, è ok.
Passiamo al controller.

Il controller

Il controller ha tre metodi:

  • formContatti(), che si occupa di visualizzare il form;
  • inviaDati(), il metodo che riceve i dati inviati dal form e che si occupa della validazione e del salvataggio dei dati, o di ritornare al form se i dati non superano la validazione;
  • conferma(), che restituisce una view di conferma all’utente.

Ecco come diventa il controller:

class ContattiController extends Controller
{
    public function formContatti()
    {
       return view('contatti');
    }

    public function inviaDati(Request $request)
    {
        // todo
    }

    public function conferma()
    {
       return view('conferma');
    }
}

Per le funzioni formContatti() e conferma() c’è poco da dire: viene restituita una view; invece il cuore di tutto il meccanismo è nel metodo inviaDati().

Tutti i dati inviati via get e via post, vengono gestiti dalla classe Request, che passiamo come dipendenza del metodo tramite (Request $request): instanziamo, in pratica, una nuovo oggetto $request che contiene dati e metodi forniti dalla classe Request.

L’oggetto $request ha a disposizione diversi metodi, tra cui:

  • $request->all(): mostra tutti i dati passati;
  • $request->has(‘nomecampo’): verifica se un determinato dato proveniente da nomecampo esiste o meno;
  • $request->input(‘nomecampo’): recupera il valore del nomecampo passato.

La validazione

La classe Validator mette a disposizione il metodo make() che si occupa di eseguire la validazione:

    $validation = Validator::make($dati_inviati, $regole);

Questo metodo richiede due parametri:

  • i dati inviati dal form
  • le regole di validazione

Per la sintassi delle regole, devi attenerti a quella fornita da Laravel, che puoi trovare nella documentazione.

Nel nostro caso, vogliamo che nome e cognome siano necessari, siano una stringa alfabetica e cha siano entrambi composti da minimo 2 caratteri; per il campo email, vogliamo che sia anch’esso necessario e che sia una stringa nel formato nome@dominio.estensione.
Su quest’ultima regola, siamo avvantaggiati: Laravel ci mette già a disposizione una regola che fa questo lavoro.

Mettiamo in una variabile le nostre regole di validazione, in un array associativo con il nome del campo come chiave e le regole come valore, separate del simbolo pipe (|):

$regole = [
    'nome'      => 'required|alpha|min:2',
    'cognome'   => 'required|alpha|min:2',
    'email'     => ‘required|email'
];

A questo punto hai proprio tutto per poter far funzionare la validazione:

$validazione = Validator::make($request->all(), $regole);

Ottimo!
Nella variabile $validazione c’è il risultato della validazione.

Hai due opzioni a questo punto:

  • $validation->passes()
  • $validation->fails()

Qui sta un po’ a te: i due metodi sono uno il contrario dell’altro, e ti servono per gestire le due situazioni: con passes() gestisci cosa deve succedere se la validazione ha successo; con fails() gestisci invece il caso in cui le cose vadano male.

Personalmente preferisco gestire la situazione della mancata validazione con fails():

if ($validation->fails()) {
	return redirect(‘contatti’)
		->withErrors($validazione)
		->withInput();
}
//codice per il caso della validazione superata

Abbastanza intuitivo, non trovi?
Nel caso la validazione fallisca, ritorna alla route contatti con gli errori della validazione e con gli input inseriti in precedenza.

Visualizzare gli errori

Siamo tornati al form perché la validazione non è stata superata.

Dobbiamo mostrare all’utente gli errori che ha commesso perché possa correggerli e reinviare nuovamente i dati.

In cima alla view, dove preferisci, inserisci questo codice:

@if (count($errors) > 0)
    <div class="alert alert-danger">
        <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
        </ul>
    </div>
@endif

@if e @endif fanno parte della sintassi di blade, il template engine di Laravel: equivalgono a if(condizione) { … }. Stessa cosa per @foreach e @endforeach.

Tramite la condizione, viene verificato che nella variabile $errors ci siano elementi.
Un secondo, fermi tutti: ma la variabile $errors da dove arriva?

Ricordi quando nel controller, nel redirect verso contatti, hai aggiunto ->withErrors() ?

Laravel ha un sistema di messaggistica che serve proprio in questi casi.
$errors è un’istanza della classe Illuminate\Support\MessageBag che fornisce utili metodi per recuperare errori o verificarne l’esistenza.

In $errors ci sono gli errori del tuo form e per recuperarli, puoi stamparli a video tramite il seguente codice:

@foreach ($errors->all() as $error)
    <li>{{ $error }}</li>
@endforeach

Il metodo all() della classe MessageBag restituisce l’array con gli errori che puoi visualizzare in un elenco puntato.

ALTERNATIVA

Se invece del blocco con gli errori a inizio form preferisci mostrare gli errori sotto ogni campo, puoi ricorrere a questa sintassi:

<input type="text" name="nome" class="form-control">
    {{ $errors->first('nome') }}

Il metodo first(‘nomecampo’) ti restituisce l’errore per quello specifico campo, permettendoti di posizionarlo dove meglio credi.

Validazione superata e salvataggio dei dati

Se l’utente fa errori, a questo punto sei tranquillo: la validazione verifica che i dati necessari ci siano e che rispettino eventuali regole.
Non rimane altro che scrivere il codice per l’inserimento dei dati nel database.

Per l’interazione con i database, Laravel ti mette a disposizione Eloquent, un’implementazione del pattern ActiveRecord.

Per ogni tabella del database puoi quindi creare un Model, ovvero una classe che estende la classe Eloquent e che eredita tutta la gamma di strumenti utili all’interazione con la tabella e quelle con le quali esistono delle relazioni.

Veniamo a noi: crea il model per la tabella contatti, tramite artisan:

artisan make:model Contatto

Per convenzione, il nome della tabella è plurale, il model è singolare.

Per i nomi inglesi, questa convenzione si concretizza nel plurale automatico da parte di Laravel: se ho un model di nome Contact, senza che io inserisca alcuna indicazione, Laravel cercherà nel database una tabella di nome contacts.

Purtroppo, se usiamo nomi italiani, non possiamo beneficiare di tale automatismo, ma c’è ovviamente una soluzione.

Apri il model appena creato: contiene una classe vuota.
Inserisci, al suo interno, la riga:

class Contatto extends Model {
    protected $table = 'contatti';
}

Quella variabile dice a Laravel che il nostro model è l’astrazione della tabella contatti.

Ed è tutto!
Che tu ci creda o no, il model è pronto e quanto hai inserito è già tutto quello che ti serve per farlo funzionare.


Torniamo al nostro controller.
Al termine del ramo condizionale if, che verifica se la validazione è andata male, scriviamo il codice per salvare i dati:

$contatto = new Contatto;
    $contatto->nome = $request->input('nome');
    $contatto->cognome = $request->input('cognome');
    $contatto->email = $request->input('email');
    $contatto->conferma_messaggi = $request->input('conferma_messaggi');
    $contatto->save();

Quando crei una nuova istanza del model, in quella variabile vengono create delle proprietà che hanno lo stesso nome dei campi della tabella.
Assegna a quelle proprietà i valori che arrivano dalla $request e infine usa il metodo save() per salvare in maniera permanente i dati nella tabella.

Il metodo save() non fa altro che eseguire una query di INSERT() ( o di UPDATE nel caso di modifica di un record).

Una volta terminate le operazioni di inserimento nella tabella, inserisci il redirect che porti alla view di conferma:

return redirect('conferma');

Conclusioni

Questo è tutto. In questo tutorial hai visto:

  • come creare una tabella tramite le migrazioni;
  • come implementare tutto il processo di sottomissione di un form e relativa validazione;
  • come creare un model per salvare i dati in una tabella di database.

Tieni a mente che questo tutorial è incentrato sulla validazione lato server con Laravel: le buone pratiche vogliono che la validazione sia duplice, ovvero sia lato client, tramite librerie javascript o con html5, sia lato server come strategia di fallback.
Nel prossimo post faremo un po’ di refactoring sul codice e ti farò vedere come usare la Form Request Validation per snellire maggiormente il codice.


Blog tags


Cerca un post


Ultimi tweet


pubblicato il {{ tweet.created_at | formattaData }}