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.
Per questo tutorial, ho ipotizzato questo scenario:
I componenti di cui hai bisogno sono, perciò:
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.
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à:
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:
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:
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 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 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:
La view, per il momento, è ok.
Passiamo al controller.
Il controller ha tre metodi:
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:
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:
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:
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.
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.
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.
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');
Questo è tutto. In questo tutorial hai visto:
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.