Validarea formularelor

Controale obligatorii

Controalele sunt marcate ca fiind obligatorii cu ajutorul metodei setRequired(), al cărei argument este textul mesajului de eroare care va fi afișat în cazul în care utilizatorul nu îl completează. Dacă nu se furnizează niciun argument, se utilizează mesajul de eroare implicit.

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

Reguli

Adăugăm reguli de validare la controale cu ajutorul metodei addRule(). Primul parametru este regula, al doilea este mesajul de eroare, iar al treilea este argumentul regulii de validare.

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

Regula de validare este verificată numai dacă utilizatorul a completat elementul.

Nette vine cu un număr de reguli predefinite ale căror nume sunt constante din clasa Nette\Forms\Form. Putem aplica aceste reguli la toate elementele:

constantă descriere argumente
Required alias al setRequired()
Filled alias setRequired()
Blank nu trebuie să fie completat
Equal valoarea este egală cu parametrul mixed
NotEqual value is not be equal to parameter mixed
IsIn valoarea este egală cu un element din matrice – – – valoarea este egală cu un parametru array
IsNotIn valoarea nu este egală cu nici un element din matrice array
Valid input passes validation (for conditions)

Intrări de text

Pentru elementele addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() se pot aplica, de asemenea, unele dintre următoarele reguli:

MinLength lungime minimă a șirului de caractere int
MaxLength lungime maximă a șirului de caractere int
Length lungime în interval sau lungime exactă pereche [int, int] sau int
Email adresa de e-mail validă
URL URL valid
Pattern se potrivește cu un model regulat string
PatternInsensitive ca și Pattern, dar nu ține cont de majuscule și minuscule string
Integer întreg
Numeric pseudonimul lui Integer
Float întreg sau număr cu virgulă mobilă
Min minimul valorii întregi int|float
Max maximum of the integer value int|float
Range valoare în intervalul de valori pereche [int|float, int|float]

Regulile Integer, Numeric și Float convertesc automat valoarea în număr întreg (sau, respectiv, flotant). În plus, regula URL acceptă și o adresă fără o schemă (de exemplu, nette.org) și completează schema (https://nette.org). Expresiile din Pattern și PatternInsensitive trebuie să fie valabile pentru întreaga valoare, adică ca și cum ar fi înfășurată în caracterele ^ and $.

Număr de articole

Pentru elementele addMultiUpload(), addCheckboxList(), addMultiSelect() puteți utiliza, de asemenea, următoarele reguli pentru a limita numărul de elemente selectate sau de fișiere încărcate:

MinLength număr minim int
MaxLength număr maxim int
Length număr în interval sau număr exact perechi [int, int] sau int

Încărcare fișier

Pentru controalele addUpload(), addMultiUpload() se pot utiliza, de asemenea, următoarele reguli:

MaxFileSize dimensiunea maximă a fișierului în bytes int
MimeType tip MIME, acceptă wildcards ('video/*') string|string[]
Image fișierul încărcat este JPEG, PNG, GIF, WebP
Pattern numele fișierului se potrivește cu o expresie regulată string
PatternInsensitive ca Pattern, dar nu ține cont de majuscule și minuscule string

MimeType și Image necesită extensia PHP fileinfo. Dacă un fișier sau o imagine este de tipul cerut este detectat prin semnătura sa. Integritatea întregului fișier nu este verificată. Puteți afla dacă o imagine nu este coruptă, de exemplu, încercând să o încărcați.

Mesaje de eroare

Toate regulile predefinite, cu excepția Pattern și PatternInsensitive, au un mesaj de eroare implicit, astfel încât acestea pot fi omise. Cu toate acestea, dacă treceți și formulați toate mesajele personalizate, veți face formularul mai ușor de utilizat.

Puteți schimba mesajele implicite în configuration, modificând textele din matricea Nette\Forms\Validator::$messages sau utilizând translator.

În textul mesajelor de eroare pot fi utilizate următoarele caractere wildcards:

%d înlocuiește treptat regulile de după argumente
%n$d înlocuiește cu al n-lea argument al regulii
%label înlocuiește cu eticheta câmpului (fără două puncte)
%name înlocuiește cu numele câmpului (de exemplu, name)
%value înlocuiește cu valoarea introdusă de utilizator
$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]);

Condiții

Pe lângă regulile de validare, pot fi stabilite și condiții. Acestea se stabilesc la fel ca regulile, însă folosim addRule() în loc de addCondition() și, bineînțeles, lăsăm fără un mesaj de eroare (condiția doar întreabă):

$form->addPassword('password', 'Password:')
	// dacă parola nu are mai mult de 8 caractere ...
	->addCondition($form::MaxLength, 8)
		// ... atunci trebuie să conțină un număr
		->addRule($form::Pattern, 'Must contain number', '.*[0-9].*');

Condiția poate fi legată de un alt element decât cel curent folosind addConditionOn(). Primul parametru este o referință la câmp. În cazul următor, e-mailul va fi necesar numai dacă caseta de selectare este bifată (adică dacă valoarea sa este true):

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

$form->addEmail('email', 'Email:')
	// dacă caseta de selectare este bifată ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... necesită e-mail
		->setRequired('Fill your email address');

Condițiile pot fi grupate în structuri complexe cu ajutorul metodelor elseCondition() și endCondition().

$form->addText(/* ... */)
	->addCondition(/* ... */) // dacă este îndeplinită prima condiție
		->addConditionOn(/* ... */) // și a doua condiție și la un alt element
			->addRule(/* ... */) // necesită această regulă
		->elseCondition() // dacă a doua condiție nu este îndeplinită
			->addRule(/* ... */) // necesită aceste reguli
			->addRule(/* ... */)
		->endCondition() // se revine la prima condiție
		->addRule(/* ... */);

În Nette, este foarte ușor să reacționați la îndeplinirea sau nu a unei condiții pe partea de JavaScript folosind metoda toggle(), consultați Dynamic JavaScript.

Trimitere la un alt element

Ca argument pentru o regulă sau o condiție, puteți trece și un alt element de formular. Regula va utiliza apoi valoarea introdusă ulterior de utilizator în browser. Acest lucru poate fi utilizat, de exemplu, pentru a valida dinamic faptul că elementul password conține același șir de caractere ca elementul password_confirm:

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

Reguli și condiții personalizate

Uneori, ne aflăm într-o situație în care regulile de validare încorporate în Nette nu sunt suficiente și trebuie să validăm datele de la utilizator în felul nostru propriu. În Nette acest lucru este foarte ușor!

Puteți trece orice callback ca prim parametru al metodelor addRule() sau addCondition(). Callback-ul acceptă elementul însuși ca prim parametru și returnează o valoare booleană care indică dacă validarea a avut succes sau nu. Atunci când se adaugă o regulă folosind addRule(), se pot trece argumente suplimentare, iar acestea sunt apoi trecute ca al doilea parametru.

Setul personalizat de validatoare poate fi astfel creat ca o clasă cu metode statice:

class MyValidators
{
	// testează dacă valoarea este divizibilă cu argumentul
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// validatori suplimentari
	}
}

Utilizarea este apoi foarte simplă:

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

Regulile de validare personalizate pot fi, de asemenea, adăugate la JavaScript. Singura cerință este ca regula să fie o metodă statică. Numele acesteia pentru validatorul JavaScript este creat prin concatenarea numelui clasei fără backslash-uri \, the underscore _, și a numelui metodei. De exemplu, scrieți App\MyValidators::validateDivisibility ca AppMyValidators_validateDivisibility și adăugați-o la obiectul Nette.validators:

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

Eveniment onValidate

După trimiterea formularului, validarea este efectuată prin verificarea regulilor individuale adăugate de addRule() și apoi prin apelarea evenimentului onValidate. Gestionarul acestuia poate fi utilizat pentru validare suplimentară, de obicei pentru a verifica combinația corectă de valori în mai multe elemente ale formularului.

În cazul în care se detectează o eroare, aceasta este transmisă formularului cu ajutorul metodei addError(). Aceasta poate fi apelată fie pe un anumit element, fie direct pe formular.

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('This combination is not possible.');
	}
}

Erori de procesare

În multe cazuri, descoperim o eroare atunci când procesăm un formular valid, de exemplu, atunci când scriem o nouă intrare în baza de date și întâlnim o cheie duplicată. În acest caz, transmitem eroarea înapoi la formular folosind metoda addError(). Aceasta poate fi apelată fie pe un element specific, fie direct pe formular:

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

Dacă este posibil, vă recomandăm să adăugați eroarea direct la elementul de formular, deoarece va apărea lângă acesta atunci când se utilizează randarea implicită.

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

Puteți apela addError() în mod repetat pentru a transmite mai multe mesaje de eroare unui formular sau element. Le obțineți cu getErrors().

Rețineți că $form->getErrors() returnează un rezumat al tuturor mesajelor de eroare, chiar și al celor transmise direct către elemente individuale, nu doar direct către formular. Mesajele de eroare transmise doar la formular sunt recuperate prin $form->getOwnErrors().

Modificarea valorilor de intrare

Utilizând metoda addFilter(), putem modifica valoarea introdusă de utilizator. În acest exemplu, vom tolera și elimina spațiile din codul poștal:

$form->addText('zip', 'Postcode:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // eliminați spațiile din codul poștal
	})
	->addRule($form::Pattern, 'The postal code is not five digits', '\d{5}');

Filtrul este inclus între regulile și condițiile de validare și, prin urmare, depinde de ordinea metodelor, adică filtrul și regula sunt apelate în aceeași ordine ca și ordinea metodelor addFilter() și addRule().

Validarea JavaScript

Limbajul regulilor și condițiilor de validare este puternic. Chiar dacă toate construcțiile funcționează atât pe partea serverului, cât și pe partea clientului, în JavaScript. Regulile sunt transferate în atributele HTML data-nette-rules ca JSON. Validarea propriu-zisă este gestionată de un alt script, care agață toate evenimentele submit ale formularului, itera peste toate intrările și execută validările respective.

Acest script este netteForms.js, care este disponibil din mai multe surse posibile:

Puteți încorpora scriptul direct în pagina HTML din CDN:

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

Sau copiați local în folderul public al proiectului (de exemplu, de la vendor/nette/forms/src/assets/netteForms.min.js):

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

Sau instalați prin npm:

npm install nette-forms

Și apoi încărcați și rulați:

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

Alternativ, îl puteți încărca direct din folderul vendor:

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

JavaScript dinamic

Doriți să afișați câmpurile de adresă doar dacă utilizatorul alege să trimită bunurile prin poștă? Nu este nicio problemă. Cheia este o pereche de metode addCondition() & toggle():

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

Acest cod spune că atunci când condiția este îndeplinită, adică atunci când caseta de selectare este bifată, elementul HTML #address-container va fi vizibil. Și viceversa. Așadar, plasăm elementele de formular cu adresa destinatarului într-un container cu acest ID, iar atunci când se face clic pe caseta de selectare, acestea sunt ascunse sau afișate. Acest lucru este gestionat de scriptul netteForms.js.

Orice selector poate fi trecut ca argument pentru metoda toggle(). Din motive istorice, un șir de caractere alfanumerice fără alte caractere speciale este tratat ca un ID de element, la fel ca și cum ar fi precedat de # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false), elementul ar fi afișat numai dacă caseta de selectare ar fi debifată.

Implementarea implicită a JavaScript modifică proprietatea hidden pentru elemente. Cu toate acestea, putem schimba cu ușurință comportamentul, de exemplu prin adăugarea unei animații. Trebuie doar să suprascrieți metoda Nette.toggle în JavaScript cu o soluție personalizată:

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

Dezactivarea validării

În anumite cazuri, este necesar să dezactivați validarea. Dacă un buton de trimitere nu trebuie să execute validarea după trimitere (de exemplu, butonul Cancel sau Preview), puteți dezactiva validarea prin apelarea $submit->setValidationScope([]). De asemenea, puteți valida formularul parțial, specificând elementele care trebuie validate.

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

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

$form->addSubmit('send1'); // Validează întregul formular
$form->addSubmit('send2')
	->setValidationScope([]); // Nu validează nimic
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Validează doar câmpul "name" (nume)
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Validează doar câmpul "vârstă".
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Validează recipientul "details" (detalii)

Evenimentul onValidate de pe formular este întotdeauna invocat și nu este afectat de setValidationScope. Evenimentul onValidate de pe container este invocat numai atunci când acest container este specificat pentru validare parțială.

versiune: 4.0