Přejít k navigační liště

Zdroják » PHP » Nette Framework: Neprůstřelné formuláře

Nette Framework: Neprůstřelné formuláře

Články PHP, Různé

Formuláře změnily Internet, z akademické sítě učinily komerční prostředí. Najednou bylo možno odesílat objednávky na e-shopech nebo poptávky přes kontaktní formuláře. Jak vypadá tvorba formuláře v Nette Framework?

Ještě než si napíšeme v Nette Framework první formulář, je potřeba říci, že formuláře jsou sice dobří sluhové, ale zlí páni. Jestliže jsem se o sessions zmiňoval jako o potenciálním zdroji největších bezpečnostních děr, o formulářích musím prohlásit to stejné. Opět ale platí, že 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ám si říci, že v této oblasti má velký náskok před ostatními frameworky.

Nette Framework 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.

Registrační formulář

Pojďme si vytvořit registrační formulář (kód si můžete stáhnout):

require 'Netteloader.php';

// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use NetteFormsForm, NetteDebug;

Debug::enable();

$countries = array(
    'Evropa' => array(
        'CZ' => 'Česká republika',
        'FR' => 'Francie',
        'DE' => 'Německo',
        'SK' => 'Slovensko',
        'GB' => 'Velká Británie',
    ),
    'AU' => 'Austrálie',
    'CA' => 'Kanada',
    '?'  => 'jiná',
);

$sex = array(
    'm' => 'muž',
    'f' => 'žena',
);

$form = new Form;
$form->addText('name', 'Jméno:');
$form->addText('age', 'Věk:');
$form->addRadioList('gender', 'Pohlaví:', $sex);
$form->addText('email', 'E-mail:');
$form->addCheckbox('promo', 'zasílejte mi reklamu');
$form->addSelect('country', 'Země:', $countries);
$form->addPassword('password', 'Heslo:');
$form->addPassword('password2', 'Heslo pro kontrolu:');
$form->addSubmit('register', 'Registrovat');

echo $form; 

Vykreslí se vám následující formulář:

Nette formulář

Upozornění: skripty musí být uloženy v UTF-8.

Samotný kód je dostatečně samovysvětlující a ve spojení s obrázkem asi nepotřebuje dalšího komentáře. Snad jen dodám, že u každé metody addXyz() představuje první parametr interní identifikátor, tedy jakési ID prvku. K jednotlivým prvkům lze poté přistupovat pomocí hranatých závorek, podobně jako k prvkům pole. Takže třeba $form['name'] je objektem třídy NetteFormsTextInput a představuje první položku formuláře.

Vykreslený formulář splňuje základní pravidlo přístupnosti – všechny popisky jsou označeny jako <label> a provázané s formulářovým prvkem. Můžete tedy myší kliknout na popisku a kurzor se přesune do prvku. Zároveň jsou ošetřeny chyby prohlížečů (tedy vlastně prohlížeče Internet Explorer) týkající se select boxu: kliknutí na popisku nebo otočení kolečkem myši nesmaže výběr. HTML podoba formuláře se dá kompletně změnit, jak si ukážeme v dalším pokračování.

Už v prvním díle seriálu jsme si říkali o životním cyklu formuláře, který začíná zrozením (tedy definicí) formuláře, pokračuje testem na to, zda byl odeslán (is submitted?) a jestli je validní (is valid?), v případě kladných odpovědí vstupní data zpracujeme, v opačném případě jej zobrazíme uživateli.

Za definici formuláře tedy vložte kód:

// jestliže byl formulář odeslán
if ($form->isSubmitted()) {
    // a jestliže jsou všechny položky vyplněny správně
    if ($form->isValid()) {
        echo '<h1>Formulář byl odeslán</h1>';

        $values = $form->getValues();
        Debug::dump($values);
        exit;
    }

} else {
    // a jestliže nebyl odeslán, nastavíme výchozí hodnoty
    $form->setDefaults(array(
        'promo' => TRUE,
    ));} 

Dotazem $form->isSubmitted() lze detekovat první zobrazení formuláře – pokud vrací false, formulář nebyl odeslán a je tedy uživateli předložen poprvé. V takovém případě mu nastavíme výchozí hodnoty metodou setDefaults. Výchozí hodnoty tvoří pole, kde jednotlivé klíče představují výše zmíněné ID prvků.

Pokud naopak formulář odeslán byl, je potřeba ještě metodou isValid() ověřit, zda byl vyplněn korektně. Existují ale případy, kdy můžeme jednat i bez ověření validace – například pokud formulář obsahuje tlačítko Cancel nebo Zpět a toto bylo stisknuto. Zda byl vícetlačítkový formulář odeslán konkrétním tlačítkem, zjistíme dotazem např.  if ($form['register']->isSubmittedBy()).

Aby bylo ověřování validace smysluplné, musíme formuláři nastavit nějaká validační pravidla. K tomu složí metoda addRule(), kde první argument říká, jakou vlastnost chceme ověřovat a druhý argument je text chybové hlášky. Asi nejčastěji se budete setkávat s pravidlem Form::FILLED, které požaduje, aby prvek byl vyplněn (nebo měl zvolenou hodnotu v případě select boxu či radio listu):

$form->addText('name', 'Jméno:')
    ->addRule(Form::FILLED, 'Zadejte jméno'); 

Zajímavostí je, že Nette Framework automaticky detekuje, že prvek je povinný, a proto mu nastaví CSS třídu required. Pokud stylem .required { color: darkred } změníme prvkům barvu, hned se nám vykreslí takto:

Nette formulář

Podobně budeme vyžadovat i povinné vyplnění věku, navíc přidáme kontrolu, zda jde o číslo ( Form::INTEGER) a zda je v povoleném rozsahu ( Form::RANGE). Zde využijeme třetí parametr metody addRule, kterým předáme validátoru informaci o rozsahu:

$form->addText('age', 'Věk:')
    ->addRule(Form::FILLED, 'Zadejte věk')
    ->addRule(Form::INTEGER, 'Věk musí být číslo')
    ->addRule(Form::RANGE, 'Věk musí být v rozmezí od 5 do 120 let', array(5, 120)); 

Zde vzniká prostor pro drobný refactoring. V chybové hlášce a třetím parametru se duplikuje číselná informace, což není nikdy dobře. Navíc v dalším pokračování se dostaneme k překládání formulářů a pokud by se hláška obsahující čísla přeložila do více jazyků, ztížila by se případná úprava intervalu. Z toho důvodu je možné použít zástupné znaky v tomto formátu:

...
    ->addRule(Form::RANGE, 'Věk musí být v rozmezí od %d do %d let', array(5, 120)); 

Dalším políčkem, které budeme chtít validovat, je e-mailová adresa. Její platnost ověřuje pravidlo  Form::EMAIL.

$form->addText('email', 'E-mail:')
    ->addRule(Form::EMAIL, 'E-mailová adresa není platná'); 

Pravidlo Form::FILLED přidáme prvkům country, password a password2. Heslo budeme ještě kontrolovat na minimální délku ( Form::MIN_LENGTH), opět s využitím zástupného znaku:

$form->addPassword('password', 'Heslo:')
    ->addRule(Form::FILLED, 'Zvolte si heslo')
    ->addRule(Form::MIN_LENGTH, 'Zadané heslo je příliš krátké, zvolte si heslo alespoň o %d znacích', 3); 

A druhé heslo zkontroluje rovnost (tj. Form::EQUAL) s heslem prvním (všimněte si odvolávky na první heslo):

$form->addPassword('password2', 'Heslo pro kontrolu:')
    ->addRule(Form::FILLED, 'Zadejte heslo ještě jednou pro kontrolu')
    ->addRule(Form::EQUAL, 'Zadané hesla se neshodují', $form['password']); 

Hotový příklad si opět můžete stáhnout a vyzkoušet. Uvidíte, že kromě validace na straně serveru se automaticky dostala ke slovu i validace na straně prohlížeče (tj. javascriptová validace). Všimněte si, že po chybové zprávě se vždy kurzor umístí do příslušného políčka.

Uživatel se dostal do kolečka na křečka: dokud formulář nevyplní správně, je mu předkládán stále dokola s výpisem chyb.

Tímto ovšem validační možnosti zdaleka nekončí. Předností Nette Framework jsou tzv. validační podmínky, pomocí nichž uděláme políčko s e-mailem nepovinné. Ty najdete v příštím pokračování.


Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Vyvíjí open-source knihovny Texy, dibi a Nette Framework a pravidelně pořádá školení pro tvůrce webových aplikací, které od podzimu 2009 nabídne kurz vývoje AJAXových aplikací.

Komentáře

Subscribe
Upozornit na
guest
21 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Kajman

Nehodilo by se i obecné validační pravidlo, kde jen zadám v parametru
regulární výraz? Nebo je raději vynecháno, aby si člověk psal jednoduché
validační funkce a nesvádělo ho to k hůře čitelným regulárům?

mike

Validace na bázi regulárních výrazů je možná, viz třeba http://forum.nettephp.com/…rms-podminky?…

Aichi

V odkazovanem foru, ktere je zavreno si lide stezuji na nemoznost validace
urcitych bloku – po odeslani jednim tlacitkem chci validovat, druhym nechci.
Tohle ma docela dobre vyreseno Prado, ma tzv. validacni skupiny, takze muzu
rict, ze tri inputy a jedno tlacitko jsou v jedne skupine a dalsi v druhe. Pak
v zavislosti na odeslani formu danym tlacitkem se provede validace jen nad temi
prvky co jsou s nim ve skupine.

Btw, jak se projevi odeslani formulare Entrem? pak tam neni zadny cudlik,
kterym byl form odeslan.

Mastodont

$form->addRadioList(‚gen­der‘, ‚Pohlaví:‘, $sex);
$form->addText(‚email‘, ‚E-mail:‘);
$form->addCheckbox(‚pro­mo‘, ‚zasílejte mi reklamu‘);
 …

Já nevím … když pominu to, že to je ikstá varianta toho, co se dá už
léta splašit třeba na phpclasses.org i jinde, mi uniká smysl toho, abych
pokaždé dynamicky vytvářel formulář, který pak vždy
vypadá stejně. Jako utilita pro tvorbu HTML šablon
formulářů to je dobré. Ale do aplikace … Co třeba metody pro vytváření
dynamických seznamů nebo check boxů, načítajích data
z databáze, jsou?

danaketh

Stejně by se dalo říct, že na HotScripts.com nebo kdekoliv jinde jich je
spousta ale většina je buď hodně mizerná nebo je jejich použití
všelijaké. Valná většina navíc postrádá alespoň základní
dokumentaci.

David: Asi by se tam dalo něco podobného najít. Já jsem to vzdal po
8 pokusech a napsal si vlastní. Manuel tam sice má třídu, která vypadá
kompletní ale nevyznám se v tom množství souborů a neměl jsem náladu
studovat použití, takže těžko říct.

Mastodont

Ano, Lemosovy třídy, dále Clonefish a samozřejmě Zend_Form. Form helpery
jsou v podstatě v každém frameworku.

Martin Hassman

Doporučuji kontaktovat naše vývojové oddělení. Oon ten celý nový
systém je pořád takový neodladěný polotvat 8-(

Mastodont

Ale já tady neřeším, kdo z vás ho má většího na základě
množství kódu a co kdo napsal dříve … pouze jsem vyjádřil své lehké
znechucení nad tím, že dynamicky generujete vždy stejný obsah – na to
můžete namítnout, že formuláře se nezobrazují tisíckrát za sekundu a
proto je cache zbytečná … nechápu, proč to odvádíte jinam.

Druhý problém mám s tím, že takto máte konfiguraci UI zadrátovanou do
kódu – pokud by si zákazník chtěl změnit popisek pole, musí do
PHP kódu.

Roman Pištěk

Jak jste, ‚sim Vás, přišel k té přezdívce? :-) Jistě, na
phpclasses.org a desítkách dalších zdrojů seženete takových rutin tuny.
Nějakou tu „vodotoňákfunguje“ třídu pro formuláře si navíc zbastlí
každý začínající programátor. Ovšem kvalitně naprogramovat a
zakomponovat do frameworku už tak snadné není.

„Co třeba metody pro vytváření dynamických seznamů nebo check boxů,
načítajích data z databáze, jsou?“ – a co třeba si takovou
specifickou záležitost naprogramovat sám? Nette tomu nijak nebrání… Pokud
ovšem nejste ten typ programátora, který celou aplikaci poslepuje z torz
z phpclasses.or­g apod.

Mastodont

Ad 1) Narážky ad hominem ve stylu „jé, ty máš blbej nick“ nemám
rád
Ad 2) Ano, to se samozřejmě napsat dá. Ale proč v Nette? :-)
Ad 3) Já cizí kód používám, nevidím důvod, proč bych měl třeba HTML
Purifier psát znovu jen proto, abych světu dokázal, že jsem těžkej borec a
všechno přede mnou bylo blbě … a je mi úplně jedno, jestli ho najdu
v PEARu, na hotscripts nebo jinde, rozhodující je licence, jednoduchost,
konsistence a celková úspora času.

cckar

Ja tedy v php neprogramuji, tak je muj dotaz asi dost nejapny, ale …

neni to trosku luxus generovat vsechno dynamicky pres php, bez spetky
hotoveho HTML? napr. napr jinak staticke formulare, veskere tagy sablonach
a pod.

Langi

Prostě a jednoduše super, jestli ne celé Nette a strukturu kterou
nabízí, tak minimálně Forms použiju určitě. Díky za ty dary.

Langi

Jen ještě rychlý dotaz – to je ok, že emailová kontrola schválí
adresu jako „lang@glavew.or­ks.czz“? Ještě bych chápal tu tečku
uprostřed, ale .czz? Jdou ty pravidla nějak editovat?

v6ak

TLD nejsou nijak předem definovány ⇒ proč ne?

Langi

Aha, ok, diky

Jakub Vrána

Pravda je ale taková, že kontrola e-mailové adresy v Nette je velmi
benevolentní. Povolí třeba i nevalidní adresu .@;.aa.

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.