Vlastní validační pravidla

Občas se dostaneme do situace, kdy nám vestavěná validační pravidla v Nette nestačí a potřebujeme data od uživatele validovat po svém. V Nette je to velmi jednoduché!

Validační pravidla přidáváme k jednotlivým komponentám formuláře pomocí metody addRule. Prvním parametrem této metody je typ pravidla – $operation. Běžně jako pravidla používáme konstanty ze třídy Form.

Jejich hodnoty začínají znakem :. Jedná se o speciální tvar, který formuláři říká, jaká metoda formulářové komponenty se má pro validaci použít. Název za dvojtečkou se doplní prefixem validate při validaci se volá jako statická metoda nad třídou formulářového prvku.

Pokud tedy přidáme na TextInput pravidlo zadané konstantou Form::FILLED, která má hodnotu :filled, bude se při validaci volat statická metoda Nette\Forms\Controls\TextInput::validateFilled() (kterou třída dědí od BaseControl).

Validační pravidlo by mělo přijímat jako první parametr instanci validované komponenty a vracet boolean hodnotu true/false. Při přidávání pravidla je možné zadat i další argumenty, ty jsou pak předány jako druhý parametr.

Pokud tedy chceme přidat vlastní pravidlo, které bude voláno touto cestou, musíme přidat třídě statickou metodu. To je však možné pouze u námi vytvořených komponent.

Protože vytvářet vlastní komponentu pouze kvůli vlastnímu validačnímu pravidlu není moc pohodlné, můžeme si pomoci jinak. Jako parametr $operation můžeme použít jakýkoliv callback, zadaný například jako řetězec (= statická metoda).

Pokud předáme callback jako řetězec, umožní to navíc i validaci na klientské straně. Skript netteForms.js v takovém případě z calbacku odstraní \ (oddělovač namespace), slavný „paamayim nekudotayim“ – :: – nahradí za podtržítko, a podívá se, zda je validátor s takovým názvem zaregistrovaný v Nette.validators.

Například pro callback MyApp\Forms\Rules::validateNumber se hledá Nette.validators.MyAppFormsRules_validateNumber. Pro vlastní zpracování pak stačí v klientském JavaScriptu tuto členskou proměnnou nastavit na callback.

Vytvoření vlastní sady validátorů se tak stává naprosto triviální záležitostí. Stačí vytvořit třídu se statickými metodami a příslušnými konstantami:

class UserFormRules
{
    const USERNAME = 'UserFormRules::validateUsername';
    const EMAIL_DOMAIN = 'UserFormRules::validateEmailDomain';

    public static function validateUsername(IControl $control)
    {
        // validace uživatelského jména
    }

    public static function validateEmailDomain(IControl $control, $domain)
    {
        // validace, zda se jedné o e-mail z domény $domain
    }
}

Použití je pak velmi jednoduché:

$form->addText('username', /* label, velikost... */)
    ->addRule(UserFormRules::USERNAME, /* validační zpráva */);
$form->addText('email', /* label, velikost... */)
    ->addRule(UserFormRules::EMAIL_DOMAIN, /* validační zpráva */, 'example.com');

Ve validačních metodách je vhodné zkontrolovat, zda je formulářový prvek vhodného typu (např. $control instanceof TextBase). Při tomto použití validačních pravidel nám totiž nic nebrání naše pravidla přidat například k souborovému inputu.

Můžeme také přidat validaci na klientovi:

Nette.validators.UserFormRules_validateUsername = function (elem, arg, value) {
    // validace uživatelského jména (value)
};

Nette.validators.UserFormRules_validateEmailDomain = function (elem, domain, value) {
    // validace, zda value je e-mail z domény arg
};