EN | CS | Přihlásit | Registrovat

Nette\Forms

Třídy Nette\Forms usnadňují vytváření a zpracování webových formulářů ve vašich aplikacích.

Třída Nette\Form je určena pro samostatné použití mimo aplikaci Nette. Pokud chcete používat formuláře v presenterech, využijte od Form odvozenou třídu Nette\Applica­tion\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\Componen­tContainer, 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 getControlPro­totype() a getLabelProto­type(), 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\Applica­tion.

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é:


Komentáře Comments feed

Jan Tvrdík | 26. 1. 2010, 23:04 | comment

Chybí seznam a popis existujících formulářových prvků.

Login to submit a comment