Convalida dei moduli

Controlli richiesti

I controlli vengono contrassegnati come obbligatori con il metodo setRequired(), il cui argomento è il testo del messaggio di errore che verrà visualizzato se l'utente non lo compila. Se non viene fornito alcun argomento, viene utilizzato il messaggio di errore predefinito.

$form->addText('name', 'Name:')
	->setRequired('Please fill your name.');

Regole

Con il metodo addRule() si aggiungono regole di convalida ai controlli. Il primo parametro è la regola, il secondo è il messaggio di errore e il terzo è l'argomento della regola di validazione.

$form->addPassword('password', 'Password:')
	->addRule($form::MinLength, 'Password must be at least %d characters', 8);

**Le regole di validazione vengono controllate solo se l'utente ha compilato l'elemento **.

Nette dispone di una serie di regole predefinite i cui nomi sono costanti della classe Nette\Forms\Form. Possiamo applicare queste regole a tutti gli elementi:

costante descrizione argomenti
Required alias di setRequired()
Filled alias di setRequired()
Blank non deve essere riempito
Equal il valore è uguale al parametro mixed
NotEqual il valore non è uguale al parametro mixed
IsIn il valore è uguale a qualche elemento dell'array array
IsNotIn il valore non è uguale a nessun elemento dell'array array
Valid l'input supera la convalida (per le condizioni)

Ingressi di testo

Per gli elementi addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() si possono applicare anche alcune delle seguenti regole:

MinLength lunghezza minima della stringa int
MaxLength lunghezza massima della stringa int
Length lunghezza nell'intervallo o lunghezza esatta coppia [int, int] o int
Email indirizzo e-mail valido
URL URL valido
Pattern corrisponde al modello regolare string
PatternInsensitive come Pattern, ma senza distinzione tra maiuscole e minuscole string
Integer numero intero
Numeric pseudonimo di Integer – –
Float numero intero o in virgola mobile
Min minimo del valore intero int|float
Max massimo del valore intero int|float
Range valore nell'intervallo coppia [int|float, int|float]

Le regole Integer, Numeric e Float convertono automaticamente il valore in numero intero (o in virgola mobile, rispettivamente). Inoltre, la regola URL accetta anche un indirizzo senza schema (ad esempio nette.org) e completa lo schema (https://nette.org). Le espressioni in Pattern e PatternInsensitive devono essere valide per l'intero valore, cioè come se fosse avvolto nei caratteri ^ and $.

Numero di articoli

Per gli elementi addMultiUpload(), addCheckboxList(), addMultiSelect() è possibile utilizzare anche le seguenti regole per limitare il numero di elementi selezionati o di file caricati:

MinLength numero minimo int
MaxLength numero massimo int
Length numero nell'intervallo o numero esatto coppie [int, int] o int

Caricamento file

Per i controlli addUpload(), addMultiUpload() si possono utilizzare anche le seguenti regole:

MaxFileSize dimensione massima del file in byte int
MimeType Tipo MIME, accetta caratteri jolly ('video/*') string|string[]
Image il file caricato è JPEG, PNG, GIF, WebP – – il nome del file corrisponde a una regola regolare.
Pattern il nome del file corrisponde a un'espressione regolare string  
PatternInsensitive come Pattern, ma senza distinzione tra maiuscole e minuscole string  

MimeType e Image richiedono l'estensione PHP fileinfo. Il fatto che un file o un'immagine sia del tipo richiesto viene rilevato dalla sua firma. L'integrità dell'intero file non viene controllata. È possibile scoprire se un'immagine non è danneggiata, ad esempio provando a caricarla.

Messaggi di errore

Tutte le regole predefinite, tranne Pattern e PatternInsensitive, hanno un messaggio di errore predefinito, quindi possono essere omesse. Tuttavia, passando e formulando tutti i messaggi personalizzati, si renderà il modulo più facile da usare.

È possibile modificare i messaggi predefiniti in configuration, modificando i testi dell'array Nette\Forms\Validator::$messages o utilizzando il traduttore.

I seguenti caratteri jolly possono essere usati nel testo dei messaggi di errore:

%d sostituisce gradualmente le regole dopo gli argomenti
%n$d sostituisce con l'argomento dell'ennesima regola
%label sostituisce l'etichetta del campo (senza i due punti)
%name sostituisce con il nome del campo (es. name)
%value sostituisce con il valore inserito dall'utente
$form->addText('name', 'Name:')
	->setRequired('Please fill in %label');

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'at least %d and no more than %d', [5, 10]);

$form->addInteger('id', 'ID:')
	->addRule($form::Range, 'no more than %2$d and at least %1$d', [5, 10]);

Condizioni

Oltre alle regole di validazione, è possibile impostare delle condizioni. Si impostano come le regole, ma si usa addRule() invece di addCondition() e, naturalmente, si lascia senza messaggio di errore (la condizione chiede solo):

$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 collegata a un elemento diverso da quello corrente usando addConditionOn(). Il primo parametro è un riferimento al campo. Nel caso seguente, l'e-mail sarà richiesta solo se la casella di controllo è selezionata (cioè il suo valore è true):

$form->addCheckbox('newsletters', 'send me newsletters');

$form->addEmail('email', 'Email:')
	// se la casella di controllo è selezionata ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... richiede l'email
		->setRequired('Inserisci il tuo indirizzo e-mail');

Le condizioni possono essere raggruppate in strutture complesse con i metodi elseCondition() e endCondition().

$form->addText(/* ... */)
	->addCondition(/* ... */) // se la prima condizione è soddisfatta
		->addConditionOn(/* ... */) // e la seconda condizione anche su un altro elemento
			->addRule(/* ... */) // richiede questa regola
		->elseCondition() // se la seconda condizione non è soddisfatta
			->addRule(/* ... */) // richiede queste regole
			->addRule(/* ... */)
		->endCondition() // si torna alla prima condizione
		->addRule(/* ... */);

In Nette, è molto facile reagire all'adempimento o meno di una condizione sul lato JavaScript, usando il metodo toggle(), vedere JavaScript dinamico.

Riferimento a un altro elemento

Come argomento per una regola o una condizione, si può passare anche un altro elemento del modulo. La regola utilizzerà il valore inserito successivamente dall'utente nel browser. Questo può essere utilizzato, ad esempio, per convalidare dinamicamente che l'elemento password contenga la stessa stringa dell'elemento password_confirm:

$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
    ->addRule($form::Equal, 'The passwords do not match', $form['password']);

Regole e condizioni personalizzate

A volte ci troviamo in una situazione in cui le regole di convalida integrate in Nette non sono sufficienti e dobbiamo convalidare i dati dell'utente a modo nostro. In Nette questo è molto semplice!

È possibile passare un qualsiasi callback come primo parametro ai metodi addRule() o addCondition(). La callback accetta l'elemento stesso come primo parametro e restituisce un valore booleano che indica se la validazione ha avuto successo. Quando si aggiunge una regola usando addRule(), si possono passare ulteriori argomenti, che vengono passati come secondo parametro.

L'insieme personalizzato di validatori può quindi essere creato 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)
	{
		// validatori aggiuntivi
	}
}

L'utilizzo è quindi molto semplice:

$form->addInteger('num')
	->addRule(
		[MyValidators::class, 'validateDivisibility'],
		'The value must be a multiple of %d',
		8,
	);

Le regole di validazione personalizzate possono essere aggiunte anche a JavaScript. L'unico requisito è che la regola deve essere un metodo statico. Il suo nome per il validatore JavaScript viene creato concatenando il nome della classe senza backslash \, the underscore _, e il nome del metodo. Ad esempio, scrivere App\MyValidators::validateDivisibility come AppMyValidators_validateDivisibility e aggiungerlo all'oggetto Nette.validators:

Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
	return val % args === 0;
};

Evento onValidate

Dopo l'invio del modulo, la validazione viene eseguita controllando le singole regole aggiunte da addRule() e richiamando l'evento onValidate. Il suo gestore può essere usato per ulteriori convalide, in genere per verificare la corretta combinazione di valori in più elementi del modulo.

Se viene rilevato un errore, questo viene passato al modulo utilizzando il metodo addError(). Questo può essere richiamato sia su un elemento specifico che direttamente sul modulo.

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

In molti casi, si scopre un errore durante l'elaborazione di un modulo valido, ad esempio quando si scrive una nuova voce nel database e si incontra una chiave duplicata. In questo caso, si trasmette l'errore al modulo con il metodo addError(). Questo metodo può essere richiamato su un elemento specifico o direttamente sul modulo:

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('Invalid password.');
	}
}

Se possibile, si consiglia di aggiungere l'errore direttamente all'elemento del form, poiché apparirà accanto a esso quando si utilizza il renderer predefinito.

$form['date']->addError('Sorry, this date is already taken.');

È possibile chiamare ripetutamente addError() per passare più messaggi di errore a un form o a un elemento. Si ottengono con getErrors().

Si noti che $form->getErrors() restituisce un riepilogo di tutti i messaggi di errore, anche quelli passati direttamente a singoli elementi, non solo direttamente al modulo. I messaggi di errore passati solo al modulo vengono recuperati con $form->getOwnErrors().

Modifica dei valori di input

Utilizzando il metodo addFilter(), possiamo modificare il valore inserito dall'utente. In questo esempio, tollereremo e rimuoveremo gli spazi nel codice postale:

$form->addText('zip', 'Codice postale:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // rimuove gli spazi dal codice postale
	})
	->addRule($form::Pattern, 'Il codice postale non è di cinque cifre', '\d{5}');

Il filtro è incluso tra le regole di validazione e le condizioni e quindi dipende dall'ordine dei metodi, cioè il filtro e la regola sono richiamati nello stesso ordine dei metodi addFilter() e addRule().

Convalida JavaScript

Il linguaggio delle regole e delle condizioni di validazione è potente. Anche se tutti i costrutti funzionano sia lato server che lato client, in JavaScript. Le regole sono trasferite in attributi HTML data-nette-rules come JSON. La validazione stessa è gestita da un altro script, che aggancia tutti gli eventi submit del modulo, itera su tutti gli input ed esegue le rispettive validazioni.

Questo script è netteForms.js, disponibile da diverse fonti:

È possibile incorporare lo script direttamente nella pagina HTML dal CDN:

<script src="https://unpkg.com/nette-forms@3"></script>

Oppure copiare localmente nella cartella pubblica del progetto (ad esempio da vendor/nette/forms/src/assets/netteForms.min.js):

<script src="/path/to/netteForms.min.js"></script>

Oppure installare tramite npm:

npm install nette-forms

E poi caricare ed eseguire:

import netteForms from 'nette-forms';
netteForms.initOnLoad();

In alternativa, è possibile caricarlo direttamente dalla cartella vendor:

import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();

JavaScript dinamico

Volete mostrare i campi dell'indirizzo solo se l'utente sceglie di inviare la merce per posta? Nessun problema. La chiave è una coppia di metodi addCondition() e toggle():

$form->addCheckbox('send_it')
	->addCondition($form::Equal, true)
		->toggle('#address-container');

Questo codice dice che quando la condizione è soddisfatta, cioè quando la casella di controllo è selezionata, l'elemento HTML #address-container sarà visibile. E viceversa. Quindi, inseriamo gli elementi del modulo con l'indirizzo del destinatario in un contenitore con quell'ID e, quando si fa clic sulla casella di controllo, essi vengono nascosti o mostrati. Questo viene gestito dallo script netteForms.js.

Qualsiasi selettore può essere passato come argomento al metodo toggle(). Per ragioni storiche, una stringa alfanumerica senza altri caratteri speciali viene trattata come un ID di elemento, come se fosse preceduta dal simbolo # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false), l'elemento verrebbe visualizzato solo se la casella di controllo fosse deselezionata.

L'implementazione predefinita di JavaScript modifica la proprietà hidden per gli elementi. Tuttavia, è possibile modificare facilmente il comportamento, ad esempio aggiungendo un'animazione. È sufficiente sovrascrivere il metodo Nette.toggle in JavaScript con una soluzione personalizzata:

Nette.toggle = (selector, visible, srcElement, event) => {
	document.querySelectorAll(selector).forEach((el) => {
		// hide or show 'el' according to the value of 'visible'
	});
};

Disabilitare la validazione

In alcuni casi, è necessario disabilitare la convalida. Se un pulsante di invio non deve eseguire la convalida dopo l'invio (per esempio il pulsante AnnullaAnteprima), si può disabilitare la convalida chiamando $submit->setValidationScope([]). È anche possibile convalidare parzialmente il modulo, specificando gli elementi da convalidare.

$form->addText('name')
	->setRequired();

$details = $form->addContainer('details');
$details->addInteger('age')
	->setRequired('age');
$details->addInteger('age2')
	->setRequired('age2');

$form->addSubmit('send1'); // Convalida l'intero modulo
$form->addSubmit('send2')
	->setValidationScope([]); // Non convalida nulla
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Valida solo il campo 'name'.
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Convalida solo il campo "età".
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Convalida il contenitore 'details'.

L'evento onValidate sul form è sempre invocato e non è influenzato dall'evento setValidationScope. onValidate sul contenitore è invocato solo quando questo contenitore è specificato per la validazione parziale.

versione: 4.0