
Validation Framework | Episodio 1
alberto • May 4, 2022
validationLa validazione degli input utente in Laravel è una componente particolarmente importante e utile del framework ed esaurirla con un semplice articolo era impossibile. Ho preferito quindi, sulla scia di Netflix e Prime Video, creare una serie di articoli di approfondimento.
Non so ancora quanti articoli usciranno, i contenuti sono tanti ed è difficile definirlo in anticipo.
Questo primo articolo si concentra sulle regole required, ovvero quelle che si occupano di controllare se gli input dell'utente sono correttamente popolati in termini di presenza. Nei prossimi episodi introdurremo altre caratteristiche del Validation Framework, legate piú al contenuto specifico.
Si validano array, non richieste utente
Iniziamo con una premessa: il Validation Framework di Laravel permette di validare strutture dati complesse, siano esse recuperate dalla request e quindi da un input utente o costruite programmaticamente all'interno del codice. Non dobbiamo quindi affrontare l'argomento pensando esclusivamente ad una form HTML, ma dobbiamo astrarci verso il concetto di struttura dati.
In particolare all'oggetto Validator
, viene passato un array associativo, eventualmente multilivello, ed è su questo che dobbiamo spostare il nostro focus:
$validator = Validator::make([
'title' => 'Laravello',
'body' => 'il miglior blog su Laravel'
], [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
Per questa ragione, possono esistere diversi scenari:
- una particolare property non viene passata
- una particolare property viene passata con un valore
null
- una particolare property viene passata con un valore stringa vuota
""
- una particolare property viene passata valorizzata
Alcuni di questi scenari non sono possibili nel caso di validazione di richiesta HTTP, principalmente grazie ad alcuni middleware che puliscono per noi la request (per esempio ConvertEmptyStringsToNull
che converte le stringhe vuote in null).
required
La regola principe legata a questo ambito è sicuramente required
.
Questa regola verifica che la property sia presente e che nessuna delle seguenti condizioni siano vere:
- la property è
null
- la property è
""
- la property è un array vuoto (o un
Countable
vuoto) - la property è un file uploadato senza path
public function test_required_missing()
{
$validator = Validator::make([
], [
'name' => 'required'
]);
$this->assertTrue($validator->fails());
}
public function test_required_empty_string()
{
$validator = Validator::make([
'name' => ''
], [
'name' => 'required'
]);
$this->assertTrue($validator->fails());
}
public function test_required_null()
{
$validator = Validator::make([
'name' => null
], [
'name' => 'required'
]);
$this->assertTrue($validator->fails());
}
Le declinazioni di required
Considerata l'importanza di questa regola di validazione, il Validation Framework presenta una serie di altre regole che si basano sulla regola appena vista.
require_if e required_unless
Queste due regole sono complementari e permettono di rendere un campo obbligatorio sulla base del valore di un altro campo:
- la declinazione
if
rende il campo obbligatorio se la condizione è vera - la declinazione
unless
rende il campo obbligatorio se la condizione è falsa
public function test_required_if()
{
$validator = Validator::make([
'age' => 10
], [
'name' => 'required_if:age,10'
]);
//diventa obbligatorio perchè age vale 10
$this->assertTrue($validator->fails());
$validator = Validator::make([
'age' => 9
], [
'name' => 'required_if:age,10'
]);
//rimane opzionale perchè age vale 9
$this->assertFalse($validator->fails());
}
public function test_required_unless()
{
$validator = Validator::make([
'age' => 10
], [
'name' => 'required_unless:age,10'
]);
//rimane opzionale perchè age vale 10
$this->assertFalse($validator->fails());
$validator = Validator::make([
'age' => 9
], [
'name' => 'required_unless:age,10'
]);
//diventa obbligatorio perchè age non vale 10
$this->assertTrue($validator->fails());
}
required_with, required_with_all, required_without, required_without_all
Questo secondo gruppo di regole permette di rendere un campo obbligatorio qualora altri campi siano valorizzati:
- la declinazione
with
rende il campo obbligatorio se almeno uno dei campi è valorizzato - la declinazione
with_all
rende il campo obbligatorio se tutti i campi sono valorizzati - la declinazione
without
rende il campo obbligatorio se almeno uno dei campi non è valorizzato - la declinazione
without_all
rende il campo obbligatorio se tutti i campi non sono valorizzati
public function test_required_with()
{
$validator = Validator::make([
'age' => 10
], [
'name' => 'required_with:age,city'
]);
//diventa obbligatorio perchè age è valorizzato
$this->assertTrue($validator->fails());
$validator = Validator::make([
], [
'name' => 'required_with:age,city'
]);
//rimane opzionale perchè mancano age e city
$this->assertFalse($validator->fails());
}
public function test_required_with_all()
{
$validator = Validator::make([
'age' => 10,
'city' => 'Varese'
], [
'name' => 'required_with_all:age,city'
]);
//diventa obbligatorio perchè sia age che city sono valorizzati
$this->assertTrue($validator->fails());
$validator = Validator::make([
'age' => 10
], [
'name' => 'required_with_all:age,city'
]);
//rimane opzionale perchè manca city
$this->assertFalse($validator->fails());
}
/*
ometto per leggibilità il test without e without_all
perchè opposti rispetto a with e with_all
*/
required_array_keys
Questa ultima declinazione permette di validare la presenza di chiavi all'interno di array annidiati.
public function test_required_array_keys()
{
$validator = Validator::make([
'author' => [
'age' => 20
]
], [
'author' => 'required_array_keys:name'
]);
$this->assertTrue($validator->fails());
}
Prohibited e le sue declinazioni
prohibited
rappresenta l'esatto opposto di required
. Un campo con questa regola deve essere non valorizzato o completamente assente.
La regola presenta alcune declinazioni simili a quelle viste in precedenza:
prohibited
prohibited_if
permette di invocare la regola solo se un altro campo assume un determinato valoreprohibited_unless
permette di invocare la regola solo se un altro campo non assume un determinato valoreprohibits
permette di invocare la regola solo se un altro campo è presente
public function test_prohibited()
{
$validator = Validator::make([
'name' => 'Alberto'
], [
'name' => 'prohibited'
]);
$this->assertTrue($validator->fails());
$validator = Validator::make([
'name' => ''
], [
'name' => 'prohibited'
]);
$this->assertFalse($validator->fails());
}
public function test_prohibited_if()
{
$validator = Validator::make([
'name' => 'Alberto',
'age' => 10
], [
'name' => 'prohibited_if:age,10'
]);
$this->assertTrue($validator->fails());
$validator = Validator::make([
'name' => 'Alberto',
'age' => 9
], [
'name' => 'prohibited_if:age,10'
]);
$this->assertFalse($validator->fails());
}
public function test_prohibited_unless()
{
$validator = Validator::make([
'name' => 'Alberto',
'age' => 10
], [
'name' => 'prohibited_unless:age,9'
]);
$this->assertTrue($validator->fails());
$validator = Validator::make([
'name' => 'Alberto',
'age' => 9
], [
'name' => 'prohibited_unless:age,9'
]);
$this->assertFalse($validator->fails());
}
public function test_prohibits()
{
$validator = Validator::make([
'name' => 'Alberto',
'age' => 10
], [
'name' => 'prohibits:age'
]);
$this->assertTrue($validator->fails());
$validator = Validator::make([
'name' => 'Alberto'
], [
'name' => 'prohibits:age'
]);
$this->assertFalse($validator->fails());
}
Altre regole
L'argomento non è peró esaurito qua. Esistono altre regole, non strettamente correlate a required
ma che insistono sulla presenza o meno di proprietà all'interno dell'array da validare.
filled
Con questa regola validiamo il fatto che il campo sia valorizzato solamente se presente. Qualora mancasse questa regola sarebbe comunque considerata valida.
public function test_filled()
{
$validator = Validator::make([
'name' => ''
], [
'name' => 'filled'
]);
//name non è valorizzato
$this->assertTrue($validator->fails());
$validator = Validator::make([
'name' => 'Alberto'
], [
'name' => 'filled'
]);
//name è valorizzato
$this->assertFalse($validator->fails());
$validator = Validator::make([
], [
'name' => 'filled'
]);
//name non esiste
$this->assertFalse($validator->fails());
}
present
Con questa regola validiamo il fatto che il campo sia presente, non ci interessa se valorizzato o meno.
public function test_present()
{
$validator = Validator::make([
], [
'name' => 'present'
]);
//name non è presente
$this->assertTrue($validator->fails());
$validator = Validator::make([
'name' => 'Alberto'
], [
'name' => 'present'
]);
//name è presente e valorizzato
$this->assertFalse($validator->fails());
$validator = Validator::make([
'name' => ''
], [
'name' => 'present'
]);
//name è presente anche se non valorizzato
$this->assertFalse($validator->fails());
}
nullable
La property che presenta questa regola puó essere valorizzata a null
. Questa non è una vera regola, ma una specie di placeholder per indicare che il campo assume questa caratteristica e che eventuali altre regole addizionali non devono essere invocate qualora il campo non sia valorizzato.
public function test_nullable()
{
$validator = Validator::make([
'name' => null
], [
'name' => 'nullable|string'
]);
//essendo nullabile, evito di controllare che sia una stringa
$this->assertFalse($validator->fails());
$validator = Validator::make([
'name' => null
], [
'name' => 'string'
]);
//null non è una stringa valida
$this->assertTrue($validator->fails());
}
sometimes
Sulla falsa riga di nullable
, anche sometimes
non è una vera regola di validazione ma permette di indicare una property come non sempre disponibile e di applicare eventuali altre regole solamente se presente nell'array dei dati.
public function test_sometimes()
{
$validator = Validator::make([
'name' => null
], [
'name' => 'sometimes|string'
]);
//il campo è valorizzato, quindi mi aspetto una stringa
$this->assertTrue($validator->fails());
$validator = Validator::make([
], [
'name' => 'sometimes|string'
]);
//non valido name
$this->assertFalse($validator->fails());
}
Tiriamo le somme
Per questo episodio ci fermiamo qui: non analizzeremo piú regole.
Se giunti a questo punto avete un po' di confusione in testa è normale. Gli scenari sono di piú di quelli che uno poteva immaginarsi 10 minuti fa.
Le regole sono appunto molteplici e varie perchè ci sono differenti possibilità. La maggior parte delle volte avrete a che fare solamente con le regole che si basano su required
, ma è interessante che le altre sfaccettature siano già implementate nativamente.
Nel prossimo episodio introdurremo tutti i concetti per creare regole condizionali avanzate.
PS tutti i file dei test sono disponibili in questo Github Gist