Obsah
Nette\Forms
Třídy Nette\Forms usnadňují vytváření a zpracování webových formulářů ve vašich aplikacích.
Třída Nette\Forms\Form je určena pro samostatné použití mimo aplikaci Nette. Pokud chcete používat formuláře v presenterech, využijte od ní odvozenou třídu Nette\Application\AppForm, která přidává obsluhu handlerů v presenterech.
Co všechno umějí?
- přehledně popsat formulář a jednotlivé prvky
- definovat validační pravidla, podmínky a filtry
- vytvářet vlastní validační pravidla
- validovat odeslaná data na straně serveru i klienta (tedy v JavaScriptu)
- lze zajistit vlastní obsluhu na straně JavaScriptu
- skrývání částí formuláře podle vlastních podmínek
- seskupovat prvky do skupin
- několik režimů vykreslování formulářů
- podpora multijazyčnosti
Nette Framework klade velký důraz na bezpečnost aplikací a proto vynakládá značné úsilí i pro zabezpečení formulářů. Dělá to zcela transparentně, nevyžaduje nic manuálně nastavovat a troufáme si říci, že v této oblasti má velký náskok před ostatními frameworky. Ochrání vaše aplikace před útokem Cross-Site Request Forgery (CSRF), odfiltruje ze vstupů kontrolní znaky, ujistí se, že všechny textové vstupy představují validní UTF-8 řetězce, že položky označené v select boxech skutečně patří mezi nabízené, automaticky ořeže mezery na jednořádkovém textovém políčku atd.
Začínáme
Nejprve si ukážeme, jak vytvořit jednoduchý formulář, nastavit mu validační pravidla a jak jej vykreslit.
Vytvoření formuláře
Začneme vytvořením formuláře:
$form = new Form;
Tímto se vytvořil formulář, který se metodou HTTP POST odešle na stejnou stránku, na jaké se nachází. Samozřejmě metodu i cílové URL lze změnit:
$form->setAction('/submit.php');
$form->setMethod('get');
Formulář se odešle metodou HTTP GET na adresu
/submit.php.
Jak nastavit HTML elementu <form> další atributy? Metoda
getElementPrototype() vrací element v podobě Nette\Web\Html objektu, se kterým se dá
snadno pracovat:
$form->getElementPrototype()->id = 'login-form';
Prvky formuláře
Existují dva způsoby, jak přidávat nové ovládací prvky do formuláře. Jednak můžeme využít toho, že formulář je potomkem třídy Nette\ComponentContainer, takže instance prvků lze přidávat metodou addComponent(), nebo lze použí ještě snažší cestu v podobě předpřipravených továrníček addText(), addPassword() atd. Příklad:
$form = new Form();
$form->addText('name', 'Your name:');
$form->addText('age', 'Your age:', 5);
$form->addCheckbox('send', 'Ship to address:');
$form->addSelect('country', 'Country:', $countries);
$form->addMultiSelect('category', 'Categories', $categories); // select s atributem multiple
Pokud z nějakého důvodu potřebujeme upravit html atributy prvků
formuláře, můžeme si je vytáhnout metodami
getControlPrototype() a getLabelPrototype() a dále
s nimi pracovat úplně stejně jako s objektem Html. Také lze obdobně získat Html
objekt samotného formuláře.
$name = $form['name']->getControlPrototype(); // htmlObject controlu
$name->class('anotherclass'); // alternativně: $name->class = 'myclass';
$nameLabel = $form['name']->getLabelPrototype(); // htmlObject labelu
$nameLabel->setText('Nette');
$htmlForm = $form->getElementPrototype(); // htmlObject formuláře
$htmlForm->class('superForm'); // nebo rovnou: $form->getElementPrototype()->class('superForm');
Validační pravidla
Metody addRule() a addCondition() … :
$form = new Form();
$form->addText('name', 'Your name:')
->addRule(Form::FILLED, 'Enter your name');
$form->addText('age', 'Your age:', 5)
->addRule(Form::FILLED, 'Enter your age')
->addRule(Form::NUMERIC, 'Age must be numeric')
->addRule(Form::RANGE, 'Age must be in range from %d to %d', array(10, 100));
$form->addCheckbox('send', 'Shipping address:')
->addCondition(Form::EQUAL, TRUE)
->toggle('sendBox'); // toggle HTML element 'sendBox'
$form->addText('email', 'Email:', 35)
->setEmptyValue('@')
->addCondition(Form::FILLED) // conditional rule: if is email filled, ...
->addRule(Form::EMAIL, 'E-mail is not valid'); // ... then check email
$form->addText('city', 'City:', 35)
->addConditionOn($form['send'], Form::EQUAL, TRUE) // if $form['send'] is checked
->addRule(Form::FILLED, 'Enter your shipping address'); // $form['city'] must be filled
$form->addSelect('country', 'Country:', $countries)->skipFirst(); // skip first option
// must be declared, if you want use skipFirst
$form['country']->addRule(Form::FILLED, 'Select your country');
Metody addRule() a addCondition() jako název validační operace akceptují callback nebo jméno statické funkce, díky čemuž je možné používat vlastní validační pravidla.
$form = new Form();
$form->addText('name', 'Text:', 10)
->addRule('MyClass::myValidator', 'Value %d is not allowed!', 11)
Veškerá JavaScriptová podpora byla vyseparována do samostatné třídy. Díky tomu je možné vytvořit vlastní JavaScriptový validátor nebo obsluhu událostí, lze snadno propojit vygenerovaný formulář s nějakým JavaScriptovým frameworkem a podobně. (viz fórum)
Každý HTML prvek formuláře lze před vykreslením libovolně upravit. Přístup k němu zajišťují metody getControlPrototype() a getLabelPrototype(), které vrací objekt typu Nette\Web\Html.
$form->addText('name', 'Text:', 10);
$form['name']->getControlPrototype()->style = "background: blue";
Potřebujeme-li zjistit id formulářového prvku, lze použít metodu getHtmlId().
Seskupování prvků
Seskupování elementů je snadné – stačí vytvořit skupinu a přidat do ní libovolné elementy:
$form->addGroup('Personal data')
->add($form['name'], $form['age'], $form['gender'], $form['email']);
Vlastně je to ještě jednodušší. Po vytvoření nové skupiny se tato stává aktivní a každý nově přidaný prvek je zároveň přidán i do ní. Takže formulář lze stavět tímto způsobem:
$form = new Form;
$form->addGroup('Personal data');
$form->addText('name', 'Your name:');
$form->addText('age', 'Your age:');
$form->addText('email', 'E-Mail:')->emptyValue = '@';
$form->addGroup('Shipping address');
$form->addCheckbox('send', 'Ship to address');
$form->addText('street', 'Street:', 35);
$form->addText('city', 'City:', 35);
$form->addSelect('country', 'Country:', $countries);
Skupina, kterou reprezentuje třída FormGroup, představuje množinu prvků IFormControl bez specifického sémantického významu. Význam jí tedy dodá až například renderovací rutina, která prvky vykreslí seskupené do elementů fieldset a podobně.
Aktuální skupinu lze nastavit metodou
Form::setCurrentGroup.
$form->setCurrentGroup($form->getGroup('název skupiny'));
Nebo taky elegantněji:
$group = $form->addGroup('název skupiny');
// ...
$form->setCurrentGroup($group);
Při zadání s parametrem hodnoty NULL nebudou další prvky
zadávány do žádné skupiny.
Vykreslení formuláře
Formulář definuje metodu render() a lze jej vykreslit
i konstrukcí echo $form.
echo $form;
Je možné si definovat vlastní vykreslovací handler
$form->setRenderer($ownRenderer), což je objekt s rozhraním
IFormRenderer. Výchozím vykreslovačem je
ConventionalRenderer, který není nutné explicitně
nastavovat.
Zpracování formuláře
// definice
$form = new Form();
$form->addText('name', 'Your name:');
$form->addSubmit('ok', 'Send')
->onClick[] = 'OkClicked'; // nebo 'OkClickHandler'
$form->addSubmit('cancel', 'Cancel')
->setValidationScope(FALSE)
->onClick[] = 'CancelClicked'; // nebo 'CancelClickHandler'
// alternativa:
$form->onSubmit[] = 'FormSubmitted'; // nebo 'FormSubmitHandler'
if (!$form->isSubmitted()) {
// první zobrazení, nastavíme výchozí hodnoty
$form->setDefaults($defaults);
}
// zavolá obslužné handlery (pozn. od verze 0.9.1)
$form->fireEvents();
// obslužné handlery:
function OkClicked(SubmitButton $button)
{
// submitted and valid
save($form->getValues());
redirect(...);
}
function CancelClicked(SubmitButton $button)
{
// process cancelled
redirect(...);
}
function FormSubmitted(Form $form)
{
// manual processing
if ($form['cancel']->isSubmittedBy()) ...
}
Tento způsob je vhodný pro použití v MVC implementacích jako Nette\Application.
Obslužný handler pro onSubmit lze použít v případě, kdy
formulář nemá žádné nebo právě jedno tlačítko. V odstatních
situacích bývá vhodnější využít handler onClick přímo na
tlačítku.
Handler onClick se volá před handlerem onSubmit.
Handlery se volají pouze v případě, že je odeslání validní.
Pokud odeslání validní není, volají se handlery pro události
onInvalidClick a onInvalidSubmit. Uvnitř metody
OkClicked tedy není nutné ověřovat validitu formuláře. Naopak
metoda FormSubmitted může být zavolána i v případě nevalidního
formuláře, byl-li odeslán tlačítkem Cancel.
V případě, že formulář nebyl odeslán tlačítkem (například byl odeslán přes JavaScript), nebo se tak tváří kvůli chybě v Internet Exploreru, bude Nette za odesílací tlačítko považovat první tlačítko formuláře. Tudíž obsluha přes události onClick je spolehlivá.
Obslužné handlery se vyvolají při prvním volání metody
fireEvents() (od verze 0.9.1, dříve při volání
isSubmitted()), při použití uvnitř Nette\Application vyvolání zajistí
samotný presenter. Úkolem metody isSubmitted() je zjistit, zda-li
byl formulář odeslán. Formulář se validuje až při zavolání metod
validate() nebo isValid().
Nastavení výchozích hodnot formuláře (v případě, že nebyl odeslán)
metodou setDefaults() nepřemazává ostatní výchozí hodnoty
prvků formuláře. Má ale druhý volitelný parametr, pomocí kterého se toho
dá docílit – $erase = TRUE.
Po odeslání a zpracování formuláře je vhodné stránku následně přesměrovat. Zabrání se tak nechtěnému opětovnému odeslání formuláře při přístupu na stánku z historie prohlížeče.
Data získaná metodou Form::getValues neobsahují
hodnoty formulářových tlačítek, tak je lze často rovnou použít pro
další zpracování (například vložení do databáze).
Obrana před Cross-Site Request Forgery (CSRF)
Útok spočívá v tom, že útočník naláká oběť na stránku, která vykoná požadavek (přesměrováním nebo javascriptem) na server, na kterém je oběť přihlášena. Ochrana spočívá v tom, že při požadavku se kontroluje token, jehož hodnotu útočník nemůže znát a tudíž ji nemůže ani podstrčit. Může jít třeba o náhodně vygenerované číslo, které se uloží do session.
Aktivace ochrany je velmi snadná:
$form = new Form;
...
$form->addProtection([string $message = NULL], [int $timeout = NULL]);
Jako parametr je možné uvést text chybové hlášky, která se zobrazí uživateli, pokud je detekováno neoprávněné odeslání.
Token chránící před CSRF útokem má platnost po dobu existence session. Díky tomu nebrání použití ve více oknech najednou (v rámci jedné session). Platnost je však možné zkrátit na počet sekund, které se uvedou jako druhý parametr.
Obrana by měla být aktivována pokaždé, kdy formulář mění nějaká citlivá data v aplikaci.
Viz také:
- Nette\Application\AppForm
- Nette\Forms API reference
- Nette\Forms\Form API reference
- Best practice: Formulářová tlačítka
- Vlastní vykreslování formulářů




Jan Tvrdík | 26. 1. 2010, 23:04 | comment
Chybí seznam a popis existujících formulářových prvků.