Validazione dei Form
Elementi Obbligatori
Gli elementi obbligatori vengono contrassegnati con il metodo setRequired()
, il cui argomento è il testo del Messaggi di errore che verrà visualizzato se l'utente non compila l'elemento. Se l'argomento non
viene fornito, verrà utilizzato il messaggio di errore predefinito.
$form->addText('name', 'Nome:')
->setRequired('Inserisci un nome');
Regole
Aggiungiamo regole di validazione agli elementi usando il metodo addRule()
. Il primo parametro è la regola, il
secondo è il testo del Messaggi di errore e il terzo è l'argomento della regola di
validazione.
$form->addPassword('password', 'Password:')
->addRule($form::MinLength, 'La password deve contenere almeno %d caratteri', 8);
Le regole di validazione vengono verificate solo se l'utente ha compilato l'elemento.
Nette include una serie di regole predefinite, i cui nomi sono costanti della classe Nette\Forms\Form
. Possiamo
usare queste regole per tutti gli elementi:
costante | descrizione | tipo argomento |
---|---|---|
Required |
elemento obbligatorio, alias per setRequired() |
– |
Filled |
elemento obbligatorio, alias per setRequired() |
– |
Blank |
l'elemento non deve essere compilato | – |
Equal |
il valore è uguale al parametro | mixed |
NotEqual |
il valore non è uguale al parametro | mixed |
IsIn |
il valore è uguale a uno degli elementi nell'array | array |
IsNotIn |
il valore non è uguale a nessuno degli elementi nell'array | array |
Valid |
l'elemento è compilato correttamente? (per Condizioni) | – |
Input di Testo
Per gli elementi addText()
, addPassword()
, addTextArea()
, addEmail()
,
addInteger()
, addFloat()
è possibile utilizzare anche alcune delle seguenti regole:
MinLength |
lunghezza minima del testo | int |
MaxLength |
lunghezza massima del testo | int |
Length |
lunghezza nell'intervallo o lunghezza esatta | coppia [int, int] o int |
Email |
indirizzo e-mail valido | – |
URL |
URL assoluto | – |
Pattern |
corrisponde all'espressione regolare | string |
PatternInsensitive |
come Pattern , ma case-insensitive |
string |
Integer |
valore intero | – |
Numeric |
alias per Integer |
– |
Float |
numero | – |
Min |
valore minimo dell'elemento numerico | int|float |
Max |
valore massimo dell'elemento numerico | int|float |
Range |
valore nell'intervallo | coppia [int|float, int|float] |
Le regole di validazione Integer
, Numeric
e Float
convertono direttamente il valore in
integer o float rispettivamente. Inoltre, la regola URL
accetta anche un indirizzo senza schema (es.
nette.org
) e aggiunge lo schema (https://nette.org
). L'espressione in Pattern
e
PatternIcase
deve corrispondere all'intero valore, cioè come se fosse racchiusa tra i caratteri ^
e
$
.
Numero di Elementi
Per gli elementi addMultiUpload()
, addCheckboxList()
, addMultiSelect()
è possibile
utilizzare anche le seguenti regole per limitare il numero di elementi selezionati o file caricati:
MinLength |
numero minimo | int |
MaxLength |
numero massimo | int |
Length |
numero nell'intervallo o numero esatto | coppia [int, int] o int |
Upload di File
Per gli elementi addUpload()
, addMultiUpload()
è possibile utilizzare anche le seguenti regole:
MaxFileSize |
dimensione massima del file in byte | int |
MimeType |
tipo MIME, consentiti caratteri jolly ('video/*' ) |
string|string[] |
Image |
immagine JPEG, PNG, GIF, WebP, AVIF | – |
Pattern |
il nome del file corrisponde all'espressione regolare | string |
PatternInsensitive |
come Pattern , ma case-insensitive |
string |
MimeType
e Image
richiedono l'estensione PHP fileinfo
. Rilevano se un file
o un'immagine è del tipo richiesto in base alla sua firma e non verificano l'integrità dell'intero file. È possibile
verificare se un'immagine è danneggiata, ad esempio, provando a caricarla.
Messaggi di Errore
Tutte le regole predefinite, ad eccezione di Pattern
e PatternInsensitive
, hanno un messaggio di
errore predefinito, quindi può essere omesso. Tuttavia, specificando e formulando tutti i messaggi su misura, renderete il form
più user-friendly.
Potete modificare i messaggi predefiniti nella configurazione,
modificando i testi nell'array Nette\Forms\Validator::$messages
o utilizzando un traduttore.
Nel testo dei messaggi di errore è possibile utilizzare le seguenti stringhe segnaposto:
%d |
sostituisce progressivamente con gli argomenti della regola |
%n$d |
sostituisce con l'n-esimo argomento della regola |
%label |
sostituisce con l'etichetta dell'elemento (senza i due punti) |
%name |
sostituisce con il nome dell'elemento (es. name ) |
%value |
sostituisce con il valore inserito dall'utente |
$form->addText('name', 'Nome:')
->setRequired('Compila %label');
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'almeno %d e al massimo %d', [5, 10]);
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'al massimo %2$d e almeno %1$d', [5, 10]);
Condizioni
Oltre alle regole, è possibile aggiungere anche condizioni. Queste si scrivono in modo simile alle regole, ma invece di
addRule()
usiamo il metodo addCondition()
e ovviamente non specifichiamo alcun messaggio di errore (la
condizione si limita a chiedere):
$form->addPassword('password', 'Password:')
// se la password non è più lunga di 8 caratteri
->addCondition($form::MaxLength, 8)
// allora deve contenere un numero
->addRule($form::Pattern, 'Deve contenere un numero', '.*[0-9].*');
La condizione può essere legata anche a un elemento diverso da quello corrente usando addConditionOn()
. Come
primo parametro, specifichiamo un riferimento all'elemento. In questo esempio, l'e-mail sarà obbligatoria solo se la checkbox è
selezionata (il suo valore sarà true):
$form->addCheckbox('newsletters', 'inviami le newsletter');
$form->addEmail('email', 'E-mail:')
// se la checkbox è selezionata
->addConditionOn($form['newsletters'], $form::Equal, true)
// allora richiedi l'e-mail
->setRequired('Inserisci l\'indirizzo e-mail');
È possibile creare strutture complesse di condizioni usando elseCondition()
e endCondition()
:
$form->addText(/* ... */)
->addCondition(/* ... */) // se la prima condizione è soddisfatta
->addConditionOn(/* ... */) // e la seconda condizione su un altro elemento
->addRule(/* ... */) // richiedi questa regola
->elseCondition() // se la seconda condizione non è soddisfatta
->addRule(/* ... */) // richiedi queste regole
->addRule(/* ... */)
->endCondition() // torniamo alla prima condizione
->addRule(/* ... */);
In Nette è molto facile reagire al soddisfacimento o meno di una condizione anche lato JavaScript usando il metodo
toggle()
, vedi JavaScript dinamico.
Riferimento a un Altro Elemento
Come argomento di una regola o condizione, è possibile passare anche un altro elemento del form. La regola utilizzerà quindi
il valore inserito successivamente dall'utente nel browser. In questo modo è possibile, ad esempio, validare dinamicamente che
l'elemento password
contenga la stessa stringa dell'elemento password_confirm
:
$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Conferma password')
->addRule($form::Equal, 'Le password inserite non corrispondono', $form['password']);
Regole e Condizioni Personalizzate
A volte ci troviamo in una situazione in cui le regole di validazione integrate in Nette non sono sufficienti e abbiamo bisogno di validare i dati dell'utente a modo nostro. In Nette è molto semplice!
Ai metodi addRule()
o addCondition()
è possibile passare qualsiasi callback come primo parametro.
Questo riceve l'elemento stesso come primo parametro e restituisce un valore booleano che indica se la validazione è andata a
buon fine. Quando si aggiunge una regola usando addRule()
, è possibile specificare anche altri argomenti, che
vengono poi passati come secondo parametro.
Possiamo quindi creare il nostro set di validatori come una classe con metodi statici:
class MyValidators
{
// verifica se il valore è divisibile per l'argomento
public static function validateDivisibility(BaseControl $input, $arg): bool
{
return $input->getValue() % $arg === 0;
}
public static function validateEmailDomain(BaseControl $input, $domain)
{
// altri validatori
}
}
L'uso è quindi molto semplice:
$form->addInteger('num')
->addRule(
[MyValidators::class, 'validateDivisibility'],
'Il valore deve essere un multiplo di %d',
8,
);
Le regole di validazione personalizzate possono essere aggiunte anche a JavaScript. La condizione è che la regola sia un
metodo statico. Il suo nome per il validatore JavaScript viene creato unendo il nome della classe senza backslash \
,
un underscore _
e il nome del metodo. Ad esempio, App\MyValidators::validateDivisibility
lo scriviamo
come AppMyValidators_validateDivisibility
e lo aggiungiamo all'oggetto Nette.validators
:
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
return val % args === 0;
};
Evento onValidate
Dopo l'invio del form, viene eseguita la validazione, durante la quale vengono controllate le singole regole aggiunte tramite
addRule()
e successivamente viene attivato l'evento
onValidate
. Il suo handler può essere utilizzato per una validazione aggiuntiva, tipicamente per verificare la
corretta combinazione di valori in più elementi del form.
Se viene rilevato un errore, lo passiamo al form usando il metodo addError()
. Questo può essere chiamato su un
elemento specifico o direttamente sul form.
protected function createComponentSignInForm(): Form
{
$form = new Form;
// ...
$form->onValidate[] = [$this, 'validateSignInForm'];
return $form;
}
public function validateSignInForm(Form $form, \stdClass $data): void
{
if ($data->foo > 1 && $data->bar > 5) {
$form->addError('Questa combinazione non è possibile.');
}
}
Errori durante l'Elaborazione
In molti casi, veniamo a conoscenza di un errore solo nel momento in cui elaboriamo un form valido, ad esempio quando inseriamo
un nuovo elemento nel database e incontriamo una duplicazione di chiavi. In tal caso, passiamo nuovamente l'errore al form usando
il metodo addError()
. Questo può essere chiamato su un elemento specifico o direttamente sul form:
try {
$data = $form->getValues();
$this->user->login($data->username, $data->password);
$this->redirect('Home:');
} catch (Nette\Security\AuthenticationException $e) {
if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
$form->addError('Password non valida.');
}
}
Se possibile, si consiglia di allegare l'errore direttamente all'elemento del form, poiché verrà visualizzato accanto ad esso quando si utilizza il renderer predefinito.
$form['date']->addError('Siamo spiacenti, ma questa data è già occupata.');
Potete chiamare addError()
ripetutamente per passare più messaggi di errore al form o all'elemento. Li ottenete
usando getErrors()
.
Attenzione, $form->getErrors()
restituisce un riepilogo di tutti i messaggi di errore, anche quelli passati
direttamente ai singoli elementi, non solo direttamente al form. I messaggi di errore passati solo al form si ottengono tramite
$form->getOwnErrors()
.
Modifica dell'Input
Usando il metodo addFilter()
possiamo modificare il valore inserito dall'utente. In questo esempio, tollereremo e
rimuoveremo gli spazi nel CAP (Codice di Avviamento Postale):
$form->addText('zip', 'CAP:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // rimuoviamo gli spazi dal CAP
})
->addRule($form::Pattern, 'Il CAP non è nel formato di cinque cifre', '\d{5}');
Il filtro viene inserito tra le regole di validazione e le condizioni, quindi l'ordine dei metodi è importante, cioè il
filtro e la regola vengono chiamati nello stesso ordine in cui sono presenti i metodi addFilter()
e
addRule()
.
Validazione JavaScript
Il linguaggio per formulare condizioni e regole è molto potente. Tutte le costruzioni funzionano sia lato server che lato
JavaScript. Vengono trasmesse negli attributi HTML data-nette-rules
come JSON. La validazione stessa viene quindi
eseguita da uno script che intercetta l'evento submit
del form, scorre i singoli elementi ed esegue la validazione
appropriata.
Questo script è netteForms.js
ed è disponibile da diverse fonti possibili:
Potete inserire lo script direttamente nella pagina HTML da una CDN:
<script src="https://unpkg.com/nette-forms@3"></script>
Oppure copiarlo localmente nella cartella pubblica del progetto (ad es. da
vendor/nette/forms/src/assets/netteForms.min.js
):
<script src="/path/to/netteForms.min.js"></script>
Oppure installarlo tramite npm:
npm install nette-forms
E successivamente caricarlo ed eseguirlo:
import netteForms from 'nette-forms';
netteForms.initOnLoad();
In alternativa, potete caricarlo direttamente dalla cartella vendor
:
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
JavaScript Dinamico
Volete visualizzare i campi per l'inserimento dell'indirizzo solo se l'utente sceglie di spedire la merce per posta? Nessun
problema. La chiave è la coppia di metodi addCondition()
& toggle()
:
$form->addCheckbox('send_it')
->addCondition($form::Equal, true)
->toggle('#address-container');
Questo codice dice che quando la condizione è soddisfatta, cioè quando la checkbox è selezionata, l'elemento HTML
#address-container
sarà visibile. E viceversa. Quindi, posizioniamo gli elementi del form con l'indirizzo del
destinatario in un container con questo ID e, facendo clic sulla checkbox, verranno nascosti o visualizzati. Questo è gestito
dallo script netteForms.js
.
Come argomento del metodo toggle()
è possibile passare qualsiasi selettore. Per motivi storici, una stringa
alfanumerica senza altri caratteri speciali viene intesa come ID dell'elemento, cioè come se fosse preceduta dal carattere
#
. Il secondo parametro opzionale consente di invertire il comportamento, cioè se usassimo
toggle('#address-container', false)
, l'elemento verrebbe visualizzato solo se la checkbox non fosse selezionata.
L'implementazione predefinita in JavaScript modifica la proprietà hidden
degli elementi. Tuttavia, possiamo
facilmente modificare il comportamento, ad esempio aggiungendo un'animazione. Basta sovrascrivere il metodo
Nette.toggle
in JavaScript con la propria soluzione:
Nette.toggle = (selector, visible, srcElement, event) => {
document.querySelectorAll(selector).forEach((el) => {
// nascondiamo o mostriamo 'el' in base al valore 'visible'
});
};
Disabilitazione della Validazione
A volte può essere utile disabilitare la validazione. Se la pressione del pulsante di invio non deve eseguire la validazione
(adatto per i pulsanti Annulla o Anteprima), la disabilitiamo con il metodo
$submit->setValidationScope([])
. Se deve eseguire solo una validazione parziale, possiamo specificare quali campi
o container del form devono essere validati.
$form->addText('name')
->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')
->setRequired('età');
$details->addInteger('age2')
->setRequired('età2');
$form->addSubmit('send1'); // Valida l'intero form
$form->addSubmit('send2')
->setValidationScope([]); // Non valida affatto
$form->addSubmit('send3')
->setValidationScope([$form['name']]); // Valida solo l'elemento name
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]); // Valida solo l'elemento age
$form->addSubmit('send5')
->setValidationScope([$form['details']]); // Valida il container details
setValidationScope
non influisce sull'evento onValidate del form, che verrà
chiamato sempre. L'evento onValidate
del container verrà attivato solo se questo container è contrassegnato per la
validazione parziale.