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.