Potrjevanje obrazcev

Zahtevani kontrolni elementi

Kontrole so označene kot zahtevane z metodo setRequired(), katere argument je besedilo sporočila o napaki, ki se prikaže, če ga uporabnik ne izpolni. Če argument ni podan, se uporabi privzeto sporočilo o napaki.

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

Pravila

Pravila potrjevanja dodajamo kontrolnim elementom z metodo addRule(). Prvi parameter je pravilo, drugi je sporočilo o napaki, tretji pa argument pravila potrjevanja.

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

Pravila potrjevanja se preverijo le, če je uporabnik izpolnil element.

Nette ima na voljo številna vnaprej določena pravila, katerih imena so konstante razreda Nette\Forms\Form. Ta pravila lahko uporabimo za vse elemente:

konstanta opis argumenti
Required vzdevek setRequired()
Filled vzdevek setRequired()
Blank ne sme biti izpolnjen
Equal vrednost je enaka parametru mixed
NotEqual vrednost ni enaka parametru mixed
IsIn vrednost je enaka nekemu elementu v polju array
IsNotIn vrednost ni enaka nobenemu elementu v polju array
Valid vhodni podatki so uspešno preverjeni (za pogoje)

Besedilni vnosi

Za elemente addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() se lahko uporabijo tudi nekatera od naslednjih pravil:

MinLength minimalna dolžina niza int
MaxLength največja dolžina niza int
Length dolžina v območju ali natančna dolžina par [int, int] ali int
Email veljavni e-poštni naslov
URL veljavni URL
Pattern ustreza regularnemu vzorcu string
PatternInsensitive kot Pattern, vendar ne upošteva velikih in malih črk string
Integer celo število
Numeric vzdevek Integer
Float celo število ali število s plavajočo vejico
Min najmanjša vrednost celega števila int|float
Max največja vrednost celega števila int|float
Range vrednost v območju par [int|float, int|float]

Pravila Integer, Numeric in Float samodejno pretvorijo vrednost v celo število (oziroma plavajoče število). Poleg tega pravilo URL sprejme tudi naslov brez sheme (npr. nette.org) in dopolni shemo (https://nette.org). Izraza v Pattern in PatternInsensitive morata biti veljavna za celotno vrednost, tj. kot da bi bila zavita v znake ^ and $.

Število elementov

Za elemente addMultiUpload(), addCheckboxList(), addMultiSelect() lahko za omejitev števila izbranih elementov ali naloženih datotek uporabite tudi naslednja pravila:

MinLength najmanjše število int
MaxLength največje število int
Length število v razponu ali natančno število pari [int, int] ali int

Prenos datotek

Za kontrole addUpload(), addMultiUpload() se lahko uporabljajo tudi naslednja pravila:

MaxFileSize največja velikost datoteke v bajtih int
MimeType vrsta MIME, sprejema nadomestne znake ('video/*') string|string[]
Image naložena datoteka je JPEG, PNG, GIF, WebP
Pattern ime datoteke ustreza regularnemu izrazu string
PatternInsensitive kot Pattern, vendar se pri tem ne upošteva velikih in malih črk string

Za MimeType in Image je potrebna razširitev PHP fileinfo. Ali je datoteka ali slika zahtevane vrste, se ugotovi na podlagi njenega podpisa. Celovitost celotne datoteke se ne preverja. Ali slika ni poškodovana, lahko ugotovite na primer tako, da jo poskusite naložiti.

Sporočila o napakah

Vsa vnaprej določena pravila, razen Pattern in PatternInsensitive, imajo privzeto sporočilo o napaki, zato jih lahko izpustite. Vendar pa boste s posredovanjem in oblikovanjem vseh prilagojenih sporočil naredili obrazec prijaznejši do uporabnika.

Privzeta sporočila lahko spremenite v obrazcu configuration, tako da spremenite besedila v polju Nette\Forms\Validator::$messages ali uporabite prevajalnik.

V besedilu sporočil o napakah lahko uporabite naslednje nadomestne znake:

%d postopoma nadomesti pravila za argumenti
%n$d nadomesti z n-tim argumentom pravila
%label nadomesti z oznako polja (brez dvopičja)
%name nadomesti z imenom polja (npr. name)
%value nadomesti z vrednostjo, ki jo vnese uporabnik
$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]);

Pogoji

Poleg pravil potrjevanja lahko določite tudi pogoje. Nastavljamo jih podobno kot pravila, le da namesto addCondition() uporabimo addRule() in seveda pustimo brez sporočila o napaki (pogoj samo vpraša):

$form->addPassword('password', 'Password:')
	// če geslo ni daljše od 8 znakov ...
	->addCondition($form::MaxLength, 8)
		// ... potem mora vsebovati številko
		->addRule($form::Pattern, 'Must contain number', '.*[0-9].*');

Pogoj lahko povežemo z drugim elementom od trenutnega z uporabo addConditionOn(). Prvi parameter je referenca na polje. V naslednjem primeru bo elektronsko sporočilo zahtevano le, če je potrditveno polje označeno (tj. njegova vrednost je true):

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

$form->addEmail('email', 'Email:')
	// če je potrditveno polje označeno ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... zahteva e-pošto
		->setRequired('Fill your email address');

Pogoje lahko združite v kompleksne strukture z metodama elseCondition() in endCondition().

$form->addText(/* ... */)
	->addCondition(/* ... */) // če je izpolnjen prvi pogoj.
		->addConditionOn(/* ... */) // in drugi pogoj tudi za drug element
			->addRule(/* ... */) // zahteva to pravilo
		->elseCondition() // če drugi pogoj ni izpolnjen
			->addRule(/* ... */) // zahteva ta pravila
			->addRule(/* ... */)
		->endCondition() // se vrnemo k prvemu pogoju
		->addRule(/* ... */);

V Nette je zelo enostavno reagirati na izpolnitev ali neizpolnitev pogoja na strani JavaScript z uporabo metode toggle(), glejte Dinamični JavaScript.

Sklicevanje na drug element

Kot argument za pravilo ali pogoj lahko posredujete tudi drug element obrazca. Pravilo bo nato uporabilo vrednost, ki jo bo uporabnik kasneje vnesel v brskalnik. To lahko na primer uporabite za dinamično preverjanje, ali element password vsebuje isti niz kot element password_confirm:

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

Pravila in pogoji po meri

Včasih se znajdemo v situaciji, ko vgrajena pravila potrjevanja v Nette niso dovolj in moramo podatke od uporabnika potrditi na svoj način. V Nette je to zelo enostavno!

Metodam addRule() ali addCondition() lahko kot prvi parameter posredujete katerikoli povratni klic. Povratni klic sprejme sam element kot prvi parameter in vrne logično vrednost, ki označuje, ali je bilo preverjanje uspešno. Pri dodajanju pravila z uporabo metode addRule() lahko posredujete dodatne argumente, ki se nato posredujejo kot drugi parameter.

Lastni nabor validatorjev je tako mogoče ustvariti kot razred s statičnimi metodami:

class MyValidators
{
	// preveri, ali je vrednost deljiva z argumentom
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// dodatni validatorji
	}
}

Uporaba je nato zelo preprosta:

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

Pravila potrjevanja po meri lahko dodate tudi v JavaScript. Edina zahteva je, da mora biti pravilo statična metoda. Njegovo ime za validator JavaScript se ustvari z združitvijo imena razreda brez povratnih lomk \, the underscore _, in imena metode. Na primer, App\MyValidators::validateDivisibility zapišite kot AppMyValidators_validateDivisibility in ga dodajte predmetu Nette.validators:

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

Dogodek onValidate

Ko je obrazec poslan, se preverjanje veljavnosti izvede tako, da se preverijo posamezna pravila, dodana s strani addRule(), nato pa se pokliče dogodek onValidate. Njegovega izvajalca je mogoče uporabiti za dodatno potrjevanje, običajno za preverjanje pravilne kombinacije vrednosti v več elementih obrazca.

Če je odkrita napaka, se posreduje obrazcu z metodo addError(). Ta se lahko kliče na določen element ali neposredno na obrazec.

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

Napake pri obdelavi

V številnih primerih odkrijemo napako med obdelavo veljavnega obrazca, npr. ko zapišemo nov vnos v zbirko podatkov in naletimo na podvojen ključ. V tem primeru napako posredujemo nazaj v obrazec s pomočjo metode addError(). To lahko kličemo na določen element ali neposredno na obrazec:

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

Če je mogoče, priporočamo, da napako dodate neposredno na element obrazca, saj se bo ob uporabi privzetega prikazovalnika prikazala poleg njega.

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

Za posredovanje več sporočil o napakah obrazcu ali elementu lahko večkrat pokličete addError(). Pridobite jih s getErrors().

Upoštevajte, da $form->getErrors() vrne povzetek vseh sporočil o napakah, tudi tistih, ki so posredovana neposredno posameznim elementom, ne le neposredno obrazcu. Sporočila o napakah, ki so posredovana samo obrazcu, se pridobijo prek $form->getOwnErrors().

Spreminjanje vhodnih vrednosti

Z metodo addFilter() lahko spremenimo vrednost, ki jo je vnesel uporabnik. V tem primeru bomo dopustili in odstranili presledke v poštni številki:

$form->addText('zip', 'Postcode:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // odstranite presledke iz poštne številke.
	})
	->addRule($form::Pattern, 'The postal code is not five digits', '\d{5}');

Filter je vključen med pravila in pogoje potrjevanja in je zato odvisen od vrstnega reda metod, tj. filter in pravilo se kličeta v enakem vrstnem redu, kot je vrstni red metod addFilter() in addRule().

Potrjevanje v javascriptu

Jezik pravil in pogojev potrjevanja je zelo zmogljiv. Čeprav vse konstrukcije delujejo tako na strani strežnika kot na strani odjemalca, v jeziku JavaScript. Pravila se prenašajo v atributih HTML data-nette-rules kot JSON. Za samo potrjevanje skrbi druga skripta, ki zasvoji vse dogodke obrazca submit, iterira čez vse vhode in izvede ustrezne validacije.

Ta skripta je netteForms.js, ki je na voljo iz več možnih virov:

Skripto lahko vstavite neposredno v stran HTML iz CDN:

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

Lahko pa jo kopirate lokalno v javno mapo projekta (npr. s spletne strani vendor/nette/forms/src/assets/netteForms.min.js):

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

ali namestite prek npm:

npm install nette-forms

Nato naložite in zaženite:

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

Lahko ga naložite tudi neposredno iz mape vendor:

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

Dinamični JavaScript

Ali želite prikazati polja z naslovi le, če se uporabnik odloči za pošiljanje blaga po pošti? Ni problema. Ključ je par metod addCondition() in toggle():

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

Ta koda pravi, da bo ob izpolnitvi pogoja, tj. ko je potrditveno polje označeno, viden element HTML #address-container. In obratno. Elemente obrazca z naslovom prejemnika torej postavimo v vsebnik s tem ID, in ko je potrditveno polje kliknjeno, se skrijejo ali prikažejo. Za to poskrbi skripta netteForms.js.

Metodi toggle() lahko kot argument posredujemo katerikoli selektor. Iz zgodovinskih razlogov se alfanumerični niz brez drugih posebnih znakov obravnava kot ID elementa, enako, kot če bi pred njim bil # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false), element bi se prikazal le, če bi bilo potrditveno polje odkljukano.

Privzeta implementacija JavaScript spreminja lastnost hidden za elemente. Vendar lahko obnašanje preprosto spremenimo, na primer z dodajanjem animacije. Preprosto nadomeščajte metodo Nette.toggle v jeziku JavaScript s prilagojeno rešitvijo:

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

Onemogočanje potrjevanja

V nekaterih primerih morate preverjanje onemogočiti. Če gumb za oddajo ne sme po oddaji zagnati preverjanja (na primer gumb Preklic ali Preview), lahko preverjanje onemogočite tako, da pokličete $submit->setValidationScope([]). Obrazec lahko delno potrdite tudi tako, da določite elemente, ki jih je treba potrditi.

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

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

$form->addSubmit('send1'); // Potrdi celoten obrazec
$form->addSubmit('send2')
	->setValidationScope([]); // Ne potrdi ničesar
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Potrdi samo polje 'ime'
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Potrdi samo polje "starost".
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Potrdi vsebnik "podrobnosti".

Dogodek onValidate na obrazcu se vedno sproži in nanj ne vpliva setValidationScope. Dogodek onValidate na vsebniku se sproži le, če je ta vsebnik določen za delno preverjanje.

različica: 4.0