Formuláře použité samostatně
Nette Forms zásadně usnadňují vytváření a zpracování webových formulářů ve vašich aplikacích. V této kapitole se seznámíte s používáním formulářů samostatně bez presenterů.
Pokud ale používáte presentery a Nette Application, je pro vás určen návod pro použití v presenterech.
Instalace:
composer require nette/forms
První formulář
Zkusíme si napsat jednoduchý registrační formulář. Jeho kód bude následující:
use Nette\Forms\Form;
$form = new Form;
$form->addText('name', 'Jméno:');
$form->addPassword('password', 'Heslo:');
$form->addSubmit('send', 'Registrovat');
Velmi snadno ho vykreslíme:
$form->render();
a v prohlížeči se zobrazí takto:

Formulář je objekt třídy Nette\Forms\Form (třída Nette\Application\UI\Form se používá
v presenterech). Přidali jsem do něj tzv. prvky jméno, heslo a odesílací tlačítko.
A teď formulář oživíme. Dotazem na $form->isSuccess() zjistíme, zda byl formulář odeslán a zda byl
vyplněn validně. Pokud ano, data vypíšeme. Za definici formuláře tedy doplníme:
if ($form->isSuccess()) {
echo 'Formulář byl správně vyplněn a odeslán';
$data = $form->getValues();
// $data->name obsahuje jméno
// $data->password obsahuje heslo
var_dump($data);
}
Metoda getValues($asArray = false) vrací odeslaná data v podobě objektu ArrayHash. Pokud by vám více vyhovovalo získat data jako čisté
pole, použijeme getValues(true). Proměnná $data obsahuje klíče name a
password s údaji, které vyplnil uživatel.
Obvykle data rovnou posíláme k dalšímu zpracování, což může být například vložení do databáze. Během
zpracování se ale může objevit chyba, například uživatelské jméno už je obsazené. V takovém případě chybu
předáme zpět do formuláře pomocí addError() a necháme jej vykreslit znovu, i s chybovou hláškou.
$form->addError('Omlouváme se, toto uživatelské jméno už někdo používá.');
Po zpracování formuláře přesměrujeme na další stránku. Zabrání se tak nechtěnému opětovnému odeslání formuláře tlačítkem obnovit, zpět nebo pohybem v historii prohlížeče.
Formulář se standardně odesílá metodou POST a to na stejnou stránku. Obojí se dá změnit:
$form->setAction('/submit.php');
$form->setMethod('GET');
A to je vlastně vše :-) Máme funkční a perfektně zabezpečený formulář.
Zkuste si přidat i další formulářové prvky.
Přístup k prvkům
Formulář i jednotlivé jeho prvky nazýváme komponentami. Tvoří komponentový strom, kde kořenem je právě formulář. K jednotlivým prvkům formuláře se dostaneme tímto způsobem:
$input = $form->getComponent('name');
// alternativní syntax: $input = $form['name'];
$button = $form->getComponent('send');
// alternativní syntax: $button = $form['send'];
Prvky se odstraní pomocí unset:
unset($form['name']);
Validační pravidla
Padlo tu slovo validní, ale formulář zatím žádná validační pravidla nemá. Pojďme to napravit.
Jméno bude povinné, proto je označíme metodou setRequired(), jejíž argument je text chybové hlášky,
která se zobrazí, pokud uživatel jméno nevyplní. Pokud argument neuvedeme, použije se výchozí chybová hláška.
$form->addText('name', 'Jméno:')
->setRequired('Zadejte prosím jméno');
Zkuste si odeslat formulář bez vyplněného jména a uvidíte, že se zobrazí chybová hláška a prohlížeč či server jej bude odmítat do té doby, dokud políčko nevyplníte.
Zároveň systém neošidíte tím, že do políčka napíšete třeba jen mezery. Kdepak. Nette levo- i pravostranné mezery automaticky odstraňuje. Vyzkoušejte si to. Je to věc, kterou byste měli s každým jednořádkovým inputem vždy udělat, ale často se na to zapomíná. Nette to dělá automaticky. (Můžete zkusit ošálit formulář a jako jméno poslat víceřádkový řetězec. Ani tady se Nette nenechá zmást a odřádkování změní na mezery.)
Formulář se vždy validuje na straně serveru, ale také se generuje JavaScriptová validace, která proběhne bleskově a
uživatel se o chybě dozví okamžitě, bez nutnosti formulář odesílat na server. Tohle má na starosti skript
netteForms.js. Vložte jej do stránky:
<script src="https://nette.github.io/resources/js/2/netteForms.min.js"></script>
Pokud se podíváte do zdrojového kódu stránky s formulářem, můžete si všimnout, že Nette povinné prvky vkládá do
elementů s CSS třídou required. Zkuste přidat do šablony následující stylopis a popiska „Jméno“ bude
červená. Elegantně tak uživatelům vyznačíme povinné prvky:
<style>
.required label { color: maroon }
</style>
Další validační pravidla přidáme metodou addRule(). První parametr je pravidlo, druhý je opět text
chybové hlášky a může ještě následovat argument validačního pravidla. Co se tím myslí?
Formulář rozšíříme o nové nepovinné políčko „věk“, které musí být celé číslo (addInteger())
a navíc v povoleném rozsahu ($form::RANGE). A zde právě využijeme třetí parametr metody
addRule(), kterým předáme validátoru požadovaný rozsah jako dvojici [od, do]:
$form->addInteger('age', 'Věk:')
->setRequired(false)
->addRule($form::RANGE, 'Věk musí být od 18 do 120', [18, 120]);
Pokud uživatel políčko nevyplní, nebudou se validační pravidla ověřovat, neboť prvek je nepovinný.
Zde vzniká prostor pro drobný refactoring. V chybové hlášce a ve třetím parametru jsou čísla uvedená duplicitně,
což není ideální. Pokud bychom tvořili vícejazyčné formuláře a hláška obsahující čísla by byla přeložena do
více jazyků, ztížila by se případná změna hodnot. Z toho důvodu je možné použít zástupné znaky %d a
Nette hodnoty doplní:
->addRule($form::RANGE, 'Věk musí být od %d do %d let', [18, 120]);
Vraťme se k prvku password, který taktéž učiníme povinným a ještě ověříme minimální délku hesla
($form::MIN_LENGTH), opět s využitím zástupného znaku:
$form->addPassword('password', 'Heslo:')
->setRequired('Zvolte si heslo')
->addRule($form::MIN_LENGTH, 'Heslo musí mít alespoň %d znaků', 8);
Přidáme do formuláře ještě políčko passwordVerify, kde uživatel zadá heslo ještě jednou, pro kontrolu.
Pomocí validačních pravidel zkontrolujeme, zda jsou obě hesla stejná ($form::EQUAL). A jako parametr dáme
odvolávku na první heslo pomocí hranatých závorek:
$form->addPassword('passwordVerify', 'Heslo pro kontrolu:')
->setRequired('Zadejte prosím heslo ještě jednou pro kontrolu')
->addRule($form::EQUAL, 'Hesla se neshodují', $form['password'])
->setOmitted();
Pomocí setOmitted() jsme označili prvek, na jehož hodnotě nám vlastně nezáleží a která existuje jen
z důvodu validace. Hodnota se nepředá do $data.
Tímto máme hotový plně funkční formulář s validací v PHP i JavaScriptu. Validační schopnosti Nette jsou daleko širší, dají se vytvářet podmínky, nechávat podle nich zobrazovat a skrývat části stránky atd. Vše se dozvíte v kapitole o validaci formulářů.
Výchozí hodnoty
Prvkům formuláře běžne nastavujeme výchozí hodnoty:
$form->addEmail('email', 'E-mail')
->setDefaultValue($lastUsedEmail);
Často se hodí nastavit výchozí hodnoty všem prvkům současně. Třeba když formulář slouží k editaci záznamů. Přečteme záznam z databáze a nastavíme výchozí hodnoty:
//$row = ['name' => 'John', 'age' => '33', /* ... */];
$form->setDefaults($row);
Volejte setDefaults() až po definici prvků.
Vykreslení formuláře
Standardně se formulář vykreslí jako tabulka. Jednotlivé prvky splňují základní pravidlo přístupnosti – všechny
popisky jsou zapsány jako <label> a provázané s příslušným formulářovým prvkem. Při kliknutí na
popisku se kurzor automaticky objeví ve formulářovém políčku.
Každému prvku můžeme nastavovat libovolné HTML atributy. Třeba přidat placeholder:
$form->addInteger('age', 'Věk:')
->setHtmlAttribute('placeholder', 'Prosím vyplňte věk');
Způsobů, jak vykreslit formulář, je opravdu velké množství, takže je tomu věnována samostatná kapitola o vykreslování.
Více tlačítek
Pokud má formulář více než jedno tlačítko, potřebujeme zpravidla rozlišit, které z nich bylo stlačeno. Tuto
informaci nám vrátí metoda isSubmittedBy() tlačítka:
$form->addSubmit('save', 'Uložit');
$form->addSubmit('delete', 'Smazat');
if ($form->isSuccess()) {
if ($form['save']->isSubmittedBy()) {
// ...
}
if ($form['delete']->isSubmittedBy()) {
// ...
}
}
Dotaz na $form->isSuccess() nevynechejte, ověříte tím validitu dat.
Když se formulář odešle tlačítkem Enter, bere se to jako kdyby byl odeslán prvním tlačítkem.
Ochrana před zranitelnostmi
Nette Framework klade velký důraz na bezpečnost a proto úzkostlivě dbá na dobré zabezpečení formulářů.
Kromě toho, že formuláře ochrání před útokem Cross Site Scripting (XSS) a Cross-Site Request Forgery (CSRF), dělá spoustu drobných zabezpečení, na které vy už nemusíte myslet.
Tak třeba odfiltruje ze vstupů všechny kontrolní znaky a prověří validitu UTF-8 kódování, takže data z formuláře budou vždycky čistá. U select boxů a radio listů ověřuje, že vybrané položky byly skutečně z nabízených a nedošlo k podvrhu. Už jsme zmiňovali, že u jednořádkových textových vstupů ostraňuje znaky konce řádků, které tam mohl poslat útočník. U víceřádkových vstupů zase normalizuje znaky pro konce řádků. A tak dále.
Nette za vás řeší bezpečnostní rizika, o kterých spousta programátorů ani netuší, že existují.
Zmíněný CSRF útok spočívá v tom, že útočník naláká oběť na stránku, která nenápadně v prohlížeči oběti vykoná požadavek na server, na kterém je oběť přihlášena, a server se domnívá, že požadavek vykonala oběť o své vůli. Obranu před tímto útokem zapnete snadno:
$form->addProtection();
Doporučujeme takto chránit formuláře v administrační části webu, které mění citlivá data v aplikaci. Framework se
proti útoku CSRF brání vygenerováním a ověřováním autorizačního tokenu, který se ukládá do session. Proto je nutné
před zobrazením formuláře mít otevřenou session. V administrační části webu obvykle už session nastartovaná je kvůli
přihlášení uživatele. Jinak session nastartujte metodou Nette\Http\Session::start().
Tak, máme za sebou rychlý úvod do formulářů v Nette. Zkuste se ještě podívat do adresáře examples v distrubuci, kde najdete další inspiraci.