Validace formulářů
Validace formulářových prvků
Na formulářové prvky lze kromě validačních pravidel uvedených v přehledu prvků používat ještě tyto pravidla:
Form::FILLED |
je prvek vyplněn? | |
Form::REQUIRED |
alias pro Form::FILLED |
|
Form::EQUAL |
je hodnota rovna uvedené? | mixed $value nebo $values[] |
Form::NOT_EQUAL |
prvek se nesmí rovnat zadané hodnotě | mixed $value nebo $values[] |
Form::IS_IN |
testuje, zda hodnota spadá do výčtu | mixed $value nebo $values[] |
Form::IS_NOT_IN |
hodnota nesmí spadat do výčtu | mixed $value nebo $values[] |
Form::VALID |
je prvek vyplněn správně? | |
Form::BLANK |
prvek nesmí být vyplněn |
Všem validačním pravidlům můžeme přidat vlastní chybovou hlášku, nebo se použije hláška výchozí, kterou můžete případně přepsat. U vícejazyčných formulářů se hlášky automaticky překládají.
V textu chybových hlášek lze používat i speciální zástupné řetězce:
%label | nahradí se textem popisky |
%name | nahradí se identifikátorem prvku |
%value | nahradí se zadanou hodnotou |
Kromě pravidel lze přidávat také dotazovací podmínky. Ty se zapisují podobně jako pravidla, jen místo
addRule()
použijeme metodu addCondition()
a pochopitelně neuvádíme žádnou chybovou zprávu
(podmínka se jen ptá):
$form->addPassword('password', 'Heslo:')
// pokud není heslo delší než 5 znaků
->addCondition(Form::MAX_LENGTH, 5)
// pak bude muset obsahovat číslici
->addRule(Form::PATTERN, 'Musí obsahovat číslici', '.*[0-9].*');
Podmínku je možné vázat i na jiný prvek, než ten aktuální. Stačí addCondition()
nahradit za
addConditionOn()
a jako první parametr uvést odvolávku na jiný prvek. V tomto případě se bude e-mail
vyžadovat tehdy, zaškrtne-li se checkbox (tj. jeho logická hodnota bude true):
$form->addCheckbox('newsletters', 'zasílejte mi newslettery');
$form->addEmail('email', 'E-mail:')
// pokud je checkbox zaškrtnut
->addConditionOn($form['newsletters'], Form::EQUAL, true)
// pak vyžaduj e-mail
->setRequired('Zadejte e-mailovou adresu');
Podmínky je možné negovat znakem ~
(vlnovka), tj. addCondition(~Form::NUMBER, ...)
. Také lze
z podmínek vytvářet komplexní struktury za pomoci metod elseCondition()
a endCondition()
.
Jak vidíte, jazyk pro formulování podmínek a pravidel je velice silný. Všechny konstrukce přitom fungují jak na straně serveru, tak i na straně JavaScriptu.
Můžeme si také přidat vlastní validátory. Metody addRule()
a addCondition()
totiž jako název
pravidla akceptují callback:
// uživatelský validátor: testuje, zda je hodnota dělitelná argumentem
// poznámka: toto je skutečná funkce, nikoliv metoda v presenteru
function divisibilityValidator(BaseControl $item, $arg)
{
return $item->getValue() % $arg === 0;
}
$form->addInteger('number', 'Číslo:')
->addRule('divisibilityValidator', 'Číslo musí být dělitelné %d.', 8);
JavaScript
Validační pravidla se na stranu JavaScriptu přenášejí v HTML atributech data-nette-rules
, které obsahují
JSON popisující jednotlivá pravidla nebo podmínky. Samotnou validaci pak provádí skript, který odchytí událost
submit
, projde jednotlivé prvky a vykoná příslušnou validaci. Výchozí implementací je soubor
netteForms.js
, který najdete v distribuci v adresáři src/assets
. Stačí jej tedy do stránky
zalinkovat:
<script src="/path/to/netteForms.js"></script>
Vlastní validační pravidla přidáme rozšířením objektu Nette.validators
:
<script>
Nette.validators.divisibilityValidator = function(elem, args, val) {
return val % args === 0;
};
</script>
Pokud náš validační callback v PHP je statická metoda ve třídě, tak název pro JavaScriptový validátor vytvoříme
smazáním zpětných lomítek \
a nahrazením dvojité dvojtečky za jedno podtržítko _
, např.
App\MyValidator::divisibilityValidator
zapíšeme jako AppMyValidator_divisibilityValidator
.
Úprava hodnot
Pomocí metody addFilter
můžeme upravit hodnotu ještě před samotným zpracování formuláře.
addFilter
je možno kombinovat s metodami addCondition
a addConditionOn
.
$form->addText('zip', 'PSČ:')
->addCondition($form::FILLED)
->addFilter(function ($value) {
return str_replace(' ', '', $value);
});
Když poté přístoupíme ve zpracování formuláře k jeho hodnotám, PSČ již bude zbaveno mezer.
Chyby při zpracování
V mnoha případech se o chybě dozvíme až ve chvíli, kdy zpracováváme platný formulář, například zapisujeme novou
položku do databáze a narazíme na duplicitu klíčů. V takovém případě chybu zpětně předáme do formuláře metodou
addError()
. Tu lze volat buď na konkrétním prvku, nebo přímo na formuláři:
try {
$values = $form->getValues();
$this->user->login($values->username, $values->password);
$this->redirect('Homepage:');
} catch (Nette\Security\AuthenticationException $e) {
if ($e->getCode() === Nette\Security\IAuthenticator::INVALID_CREDENTIAL) {
$form->addError('Neplatné heslo.');
}
...
}
Doporučuje se připojit chybu přímo k prvku formuláře, protože se zobrazí vedle něj při použití výchozího rendereru.
$form['date']->addError("Omlouváme se, ale toto datum již je zabrané.");
Validace celého formuláře
Pokud z nějakého důvodu potřebujete přidat validační funkcionalitu, typicky ověření správné kombinace hodnot ve
více prvcích formuláře, můžete si napsat vlastní validační funkce. Ty pak na formulář navážete pomocí události
onValidate
:
protected function createComponentSignInForm()
{
$form = new Form;
...
$form->onValidate[] = [$this, 'validateSignInForm'];
return $form;
}
public function validateSignInForm($form)
{
$values = $form->getValues();
if (...) { // validační podmínka
$form->addError('Tato kombinace není možná.');
}
}
Na událost onValidate
můžete registrovat libovolný počet funkcí. Funkce je chápána jako úspěšná, pokud
nepřidá do formuláře chybu pomocí $form->addError()
.
Vypnutí validace
Někdy se může hodit validaci vypnout. Pokud stisknutí odesílacího tlačítka nemá provádět validaci (vhodné pro
tlačítka Cancel nebo Preview), vypneme ji metodou $submit->setValidationScope([])
. Pokud má
provádět validaci jen částečnou, můžeme určit které pole nebo formulářové kontejnery se mají validovat.
$form->addText('name')->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')->setRequired('age');
$details->addInteger('age2')->setRequired('age2');
$form->addSubmit('send1'); // Validuje celý formuláře
$form->addSubmit('send2')->setValidationScope(false); // Nevaliduje vůbec
$form->addSubmit('send3')->setValidationScope([$form['name']]); // Validuje pouze pole name
$form->addSubmit('send4')->setValidationScope([$form['details']['age']]); // Validuje pouze pole age
$form->addSubmit('send5')->setValidationScope([$form['details']]); // Validuje kontejner details
setValidationScope
neovlivní událost onValidate
u formuláře, která bude zavolána vždy.
Událost onValidate
u kontejneru bude vyvolána pouze pokud je tento kontejner označen pro částečnou
validaci.