Nyomtatványok érvényesítés

Kötelező vezérlők

A vezérlőelemeket a setRequired() metódussal jelöljük meg kötelezőnek, amelynek argumentuma a hibaüzenet szövege, amely akkor jelenik meg, ha a felhasználó nem tölti ki. Ha nem adunk meg argumentumot, akkor az alapértelmezett hibaüzenetet használjuk.

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

Szabályok

A addRule() módszerrel adunk hozzá érvényesítési szabályokat a vezérlőkhöz. Az első paraméter a szabály, a második a hibaüzenet, a harmadik pedig az érvényesítési szabály argumentuma.

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

A hitelesítési szabályok csak akkor kerülnek ellenőrzésre, ha a felhasználó kitöltötte az elemet.

A Nette számos előre definiált szabályt tartalmaz, amelyek neve a Nette\Forms\Form osztály konstanciái. Ezeket a szabályokat minden elemre alkalmazhatjuk:

konstans leírás érvek
Required setRequired() álneve – –
Filled a setRequired() álneve
Blank nem szabad kitölteni
Equal érték egyenlő a paraméterrel mixed
NotEqual érték nem egyenlő a paraméterrel mixed
IsIn érték egyenlő a tömb valamelyik elemével array
IsNotIn érték nem egyenlő a tömb egyetlen elemével sem array
Valid a bemenet átmegy az érvényesítésen ( feltételek esetén)

Szöveges bemenetek

A addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() elemekre a következő szabályok közül néhányat is lehet alkalmazni:

MinLength minimális karakterlánc-hossz int
MaxLength maximális karakterlánc-hossz int
Length hossz a tartományban vagy pontos hossz pár [int, int] vagy int
Email érvényes e-mail cím
URL érvényes URL
Pattern megfelel a szabályos mintának string
PatternInsensitive mint a Pattern, de a nagy- és kisbetűket figyelmen kívül hagyja string
Integer egész szám
Numeric Integer álneve
Float egész vagy lebegőpontos szám
Min az egész szám értékének minimuma int|float
Max az egész szám értékének maximuma int|float
Range érték a tartományban pár [int|float, int|float]

A Integer, Numeric és Float szabályok automatikusan egész számra (illetve lebegőszámra) konvertálják az értéket. Továbbá a URL szabály elfogad egy séma nélküli címet is (pl. nette.org), és kiegészíti a sémát (https://nette.org). A Pattern és a PatternInsensitive kifejezéseknek a teljes értékre érvényesnek kell lenniük, azaz mintha a ^ and $ karakterekbe lenne csomagolva.

Tételek száma

A addMultiUpload(), addCheckboxList(), addMultiSelect() elemek esetében a következő szabályokkal is korlátozhatja a kiválasztott elemek vagy feltöltött fájlok számát:

MinLength minimális szám int
MaxLength maximális szám int
Length szám tartományon belül vagy pontos szám párok [int, int] vagy int

Fájl feltöltés

A addUpload(), addMultiUpload() vezérlőkre a következő szabályok is alkalmazhatók:

MaxFileSize maximális fájlméret bájtban int
MimeType MIME-típus, elfogadja a helyettesítő karaktereket ('video/*') string|string[]
Image a feltöltött fájl JPEG, PNG, GIF, WebP
Pattern a fájl neve megfelel a reguláris kifejezésnek string
PatternInsensitive mint a Pattern, de a nagy- és kisbetűket nem érzékeli string

A MimeType és a Image a fileinfo PHP kiterjesztést igényli. Az, hogy egy fájl vagy kép a kívánt típusba tartozik-e, az aláírás alapján állapítható meg. A teljes fájl sértetlenségét nem ellenőrzi a rendszer. Azt, hogy egy kép nem sérült-e, például úgy lehet megállapítani, ha megpróbáljuk betölteni.

Hibaüzenetek

A Pattern és a PatternInsensitive kivételével minden előre definiált szabály rendelkezik alapértelmezett hibaüzenettel, így ezek elhagyhatók. Az összes testreszabott üzenet átadásával és megfogalmazásával azonban felhasználóbarátabbá teszi az űrlapot.

Az alapértelmezett üzeneteket a forms:configuration-ban, a Nette\Forms\Validator::$messages tömbben lévő szövegek módosításával vagy a translator használatával módosíthatja.

A hibaüzenetek szövegében a következő helyettesítő karakterek használhatók:

%d fokozatosan helyettesíti a szabályokat az argumentumok után.
%n$d az n-edik szabály argumentumával helyettesíti.
%label a mező címkéjével helyettesíti (kettőspont nélkül)
%name a mező nevével helyettesíti (pl. name).
%value a felhasználó által megadott értékkel helyettesíti.
$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]);

Feltételek

Az érvényesítési szabályok mellett feltételek is beállíthatók. Ezek beállítása hasonlóan történik, mint a szabályoké, azonban a addCondition() helyett a addRule() címet használjuk, és természetesen hibaüzenet nélkül hagyjuk (a feltétel csak kérdez):

$form->addPassword('password', 'Jelszó:')
	// ha a jelszó nem hosszabb 8 karakternél ...
	->addCondition($form::MaxLength, 8)
		// ... akkor egy számot kell tartalmaznia.
		->addRule($form::Pattern, 'Számot kell tartalmaznia', '.*[0-9].*');

A feltétel a addConditionOn() segítségével az aktuális elemtől eltérő elemhez is kapcsolható. Az első paraméter a mezőre való hivatkozás. A következő esetben az e-mail csak akkor lesz kötelező, ha a jelölőnégyzet be van jelölve (azaz az értéke true):

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

$form->addEmail('email', 'Email:')
	// ha a jelölőnégyzet be van jelölve ...
	->addConditionOn($form['newsletters'], $form::Equal, true)
		// ... megköveteli az email
		->setRequired('Adja meg az e-mail címét');

A feltételek a elseCondition() és a endCondition() metódusokkal összetett struktúrákba csoportosíthatók.

$form->addText(/* ... */)
	->addCondition(/* ... */) // ha az első feltétel teljesül.
		->addConditionOn(/* ... */) // és a második feltétel egy másik elemre is.
			->addRule(/* ... */) // megköveteli ezt a szabályt
		->elseCondition() // ha a második feltétel nem teljesül
			->addRule(/* ... */) // szükség van ezekre a szabályokra.
			->addRule(/* ... */)
		->endCondition() // visszatérünk az első feltételhez
		->addRule(/* ... */);

A Nette-ben nagyon egyszerűen reagálhatunk egy feltétel teljesülésére vagy nem teljesülésére a JavaScript oldalán a toggle() metódus segítségével, lásd Dynamic JavaScript.

Hivatkozások a vezérlők között

A szabály vagy feltétel argumentum lehet hivatkozás egy másik elemre. Például dinamikusan érvényesítheti, hogy a text annyi karaktert tartalmazzon, ahány karakter a length mező értéke:

$form->addInteger('length');
$form->addText('text')
	->addRule($form::Length, null, $form['length']);

Egyéni szabályok és feltételek

Néha olyan helyzetbe kerülünk, amikor a Nette beépített érvényesítési szabályai nem elegendőek, és a felhasználó adatait saját módon kell érvényesítenünk. A Nette-ben ez nagyon egyszerű!

A addRule() vagy a addCondition() metódusok első paramétereként bármilyen visszahívást átadhatunk. A callback első paraméterként magát az elemet fogadja el, és egy bóluszi értéket ad vissza, amely jelzi, hogy az érvényesítés sikeres volt-e. Szabály hozzáadásakor a addRule() segítségével további argumentumok adhatók át, amelyek második paraméterként kerülnek átadásra.

Az egyéni érvényesítőkészlet tehát statikus metódusokkal rendelkező osztályként hozható létre:

class MyValidators
{
	// teszteli, hogy az érték osztható-e az argumentummal.
	public static function validateDivisibility(BaseControl $input, $arg): bool
	{
		return $input->getValue() % $arg === 0;
	}

	public static function validateEmailDomain(BaseControl $input, $domain)
	{
		// további validátorok
	}
}

A használat nagyon egyszerű:

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

Egyéni érvényesítési szabályokat is hozzá lehet adni a JavaScripthez. Az egyetlen követelmény, hogy a szabály statikus metódus legyen. Nevét a JavaScript-érvényesítő számára az osztály neve (backslashes nélkül) \, the underscore _, és a metódus neve egymás mellé fűzésével hozzuk létre. Például írjuk a App\MyValidators::validateDivisibility -t AppMyValidators_validateDivisibility néven, és adjuk hozzá a Nette.validators objektumhoz:

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

Esemény onValidate

Az űrlap elküldése után az érvényesítés a addRule() által hozzáadott egyedi szabályok ellenőrzésével, majd a onValidateesemény meghívásával történik. Kezelője további érvényesítésre használható, jellemzően több űrlapelem értékének helyes kombinációjának ellenőrzésére.

Ha hibát észlel, azt a addError() metódus segítségével továbbítja az űrlapnak. Ezt vagy egy adott elemen, vagy közvetlenül az űrlapon lehet meghívni.

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('Ez a kombináció nem lehetséges.');
	}
}

Hibák feldolgozása

Sok esetben egy érvényes űrlap feldolgozása közben fedezünk fel hibát, például amikor új bejegyzést írunk az adatbázisba, és egy duplikált kulcsot találunk. Ebben az esetben a addError() metódus segítségével adjuk vissza a hibát az űrlapnak. Ezt vagy egy adott elemen, vagy közvetlenül az űrlapon hívhatjuk meg:

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

Ha lehetséges, javasoljuk, hogy a hibát közvetlenül az űrlap elemhez adjuk hozzá, mivel az alapértelmezett renderelő használata esetén a hiba mellette fog megjelenni.

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

A addError() többször is meghívható, hogy több hibaüzenetet adjon át egy űrlapnak vagy elemnek. Ezeket a getErrors() segítségével kapja meg.

Vegye figyelembe, hogy a $form->getErrors() az összes hibaüzenet összefoglalóját adja vissza, még a közvetlenül az egyes elemeknek átadottakat is, nem csak közvetlenül az űrlapnak. A csak az űrlapnak átadott hibaüzeneteket a $form->getOwnErrors() segítségével kapjuk meg.

A beviteli értékek módosítása

A addFilter() módszerrel módosíthatjuk a felhasználó által megadott értéket. Ebben a példában megtűrjük és eltávolítjuk a szóközöket az irányítószámban:

$form->addText('zip', 'Irányítószám:')
	->addFilter(function ($value) {
		return str_replace(' ', '', $value); // eltávolítjuk a szóközöket az irányítószámból.
	})
	->addRule($form::Pattern, 'Az irányítószám nem ötjegyű', '\d{5}');

A szűrő az érvényesítési szabályok és feltételek között szerepel, ezért függ a módszerek sorrendjétől, azaz a szűrő és a szabály meghívása ugyanabban a sorrendben történik, mint a addFilter() és a addRule() módszerek sorrendje.

JavaScript érvényesítés

Az érvényesítési szabályok és feltételek nyelve nagy teljesítményű. Annak ellenére, hogy minden konstrukció szerver- és kliensoldalon egyaránt működik, JavaScriptben. A szabályok átadása HTML-attribútumokban történik data-nette-rules JSON-ként. Magát az érvényesítést egy másik szkript kezeli, amely az űrlap összes submit eseményét megakasztja, iterálja az összes bemenetet és lefuttatja a megfelelő érvényesítéseket.

Ez a szkript a netteForms.js, amely több lehetséges forrásból is elérhető:

A szkriptet közvetlenül a CDN-ből származó HTML-oldalba ágyazhatja be:

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

Vagy másolja be helyileg a projekt nyilvános mappájába (pl. a vendor/nette/forms/src/assets/netteForms.min.js):

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

Vagy telepítse az npm segítségével:

npm install nette-forms

Aztán töltsd be és futtasd:

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

Alternatívaként közvetlenül a vendor mappából is betöltheti:

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

Dinamikus JavaScript

Csak akkor szeretné megjeleníteni a cím mezőt, ha a felhasználó úgy dönt, hogy postai úton küldi el az árut? Nem probléma. A kulcs a addCondition() & toggle() metóduspár:

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

Ez a kód azt mondja, hogy ha a feltétel teljesül, vagyis ha a jelölőnégyzet be van jelölve, akkor a #address-container HTML-elem láthatóvá válik. És fordítva. Tehát a címzett címét tartalmazó űrlapelemeket egy ilyen azonosítóval rendelkező konténerbe helyezzük, és amikor a jelölőnégyzetre kattintunk, elrejtjük vagy megjelenítjük őket. Ezt a netteForms.js szkript kezeli.

A toggle() metódusnak argumentumként bármilyen szelektor átadható. Történelmi okokból a más speciális karaktereket nem tartalmazó alfanumerikus karakterláncot elemazonosítóként kezeljük, ugyanúgy, mintha a # character. The second optional parameter allows us to reverse the behavior, i.e. if we used toggle('#address-container', false) lenne előtte, az elem csak akkor jelenne meg, ha a jelölőnégyzet nincs bejelölve.

Az alapértelmezett JavaScript implementáció megváltoztatja az elemek hidden tulajdonságát. A viselkedést azonban könnyen megváltoztathatjuk, például egy animáció hozzáadásával. Csak írjuk felül a Nette.toggle metódust JavaScriptben egy egyéni megoldással:

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

Az érvényesítés letiltása

Bizonyos esetekben le kell tiltani az érvényesítést. Ha egy beküldőgombnak nem kellene érvényesítést futtatnia a beküldés után (például Mondás vagy Közlemény gomb), akkor az érvényesítést a $submit->setValidationScope([]) meghívásával tilthatja le. Az űrlapot részlegesen is érvényesítheti az érvényesítendő elemek megadásával.

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

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

$form->addSubmit('send1'); // Az egész űrlap hitelesítése
$form->addSubmit('send2')
	->setValidationScope([]); // Nem érvényesít semmit.
$form->addSubmit('send3')
	->setValidationScope([$form['name']]); // Csak a 'name' mezőt érvényesíti.
$form->addSubmit('send4')
	->setValidationScope([$form['details']['age']]); // Csak az 'age' mezőt érvényesíti.
$form->addSubmit('send5')
	->setValidationScope([$form['details']]); // Validálja a 'details' konténert.

Az onValidate esemény az űrlapon mindig meghívásra kerül, és nem befolyásolja a setValidationScope. onValidate esemény a konténeren csak akkor hívódik meg, ha ez a konténer részleges érvényesítésre van megadva.

verzió: 4.0