Nájsť recept na správnu, rozširovateľnú a relatívne pohodlnú implementáciu formulárového modulu dá fakt zabrať. Pre Nette ujo Grudl síce vykročil, ale ja som to skúsil inak. Z fleku exámpl:
$document = new Container;
$document['title'] = new String;
$document['body'] = new Text;
Formulár z tohto pohľadu je definícia hodnôt a ich validácii. Ako si
pozorní určite všimli, formulár nedefinuje názvy políčok, ani atribúty
ako class, id, rows či
cols. To všetko nechávam na šablonku. Čo nás zaujíma sú typy
hodnôt ktoré očakávame: polia, reťazce, dátumy, súbory, atp. Nad týmito
primitívnymi typmi následne fungujú validácie a filtre.
Hodnoty
Hodnoty sa čítajú z poľa hodnôt a je im jedno či je to POST,
databáza, súbor alebo obyčajné pole. Na každom políčku je k dispozícii
dvojička metód getValue a setValue, ktoré pracujú
s obyčajnými poľami.
Integrácia s Nette
Klasickú formulárovú funkčnosť a integráciu do Nette zabezpečuje
doplnková trieda Form. Form rieši komunikáciu
s prezentérom, získavanie a distribúciu HTTP dát do políčok, nastavovanie
action, či method atribútov. Form
v sebe zapúzdruje inštanciu triedy Container, čiže zvonku sa
javí prirodzene:
$form = new Form;
$form['document'] = new Container;
$form['document']['title'] = new String;
$form['document']['body'] = new Text;
if( $form->isSubmitted() )
{
if( !$form->hasErrors() )
{
// Save data
}
}
else
{
$form['document']->setValue( array
(
'title' => 'Default title',
'body' => 'Default body'
));
}
Šablony
Na zjednodušenie práce mám FormHelper, ktorý drobnosti so
šablonou rieši za mňa. V žiadnom prípade sa však nesnaží definovať
niečo navyše, výstupom sú holé políčka s nastavenými tými
najnutnejšími atribútmi:
{widget:label $form['document']['title']}Titulok{/widget:label}
{widget:message:error $form['document']['title']}
{widget:text $form['document']['title'], 'width' => '100%'}
{widget:label $form['document']['body']}Body{/widget:label}
{widget:message:error $form['document']['body']}
{widget:text:area $form['document']['body'], 'rows' => 5, 'cols' => 40}
Až v šablone sa priraďuje políčku jeho vzhľad, spôsob rozloženia
prvkov. Je to síce pracnejšie ako s ConventionalRenderer z Nette
Forms, ale zato máte plnú voľnosť. FormHelper tiež zverejňuje
funkcie na generovanie `name, id, style,
ktoré sa dajú ľahko využiť pre písanie vlastných widgetov.
Políčka
Okrem obligátnych skalárnych, modul má aj komplikovanejšie políčka.
Jedným z nich je ElasticContainer a pomôže ak UI vyžaduje
pridávať vopred neurčený počet príloh, e-mailov, alebo tagov (každý
v samostatnom políčku).
$form['emails'] = new ElasticContainer( function() {
$prototype = new String;
$prototype->validates( 'email' );
return $prototype;
});
Základná sada políčok by mala stačiť na všetko, ak však chcete, nič
vám nebráni si nadefinovať vlastnú triedu ktorá dedí od
Field.
Validácie
Validácie to vie samozrejme tiež:
$form->validates( 'children', array
(
'document/title' => array
(
'presence' => true,
'length' => array( 'minimum' => 3 )
),
'document/body' => array
(
'presence' => true
)
));
Validácie je možné viazať na rodičovské, ale aj na detské políčka. Horný príklad je možné zapísať hneď niekoľkými spôsobmi:
// Naviazanim na 'document'; rodica 'title' a 'body' policok
$form['document']->validates( 'children', array
(
'title' => array
(
'presence' => true,
'length' => array( 'minimum' => 3 )
),
'body' => array
(
'presence' => true
)
));
// --- alebo naviazanim na samotne policko s pouzitim pola
$form['document']['title']->validates( array
(
'presence' => true,
'length' => array( 'minimum' => 3 )
));
$form['document']['body']->validates( array
(
'presence' => true
));
// --- alebo postupnym volanim metody validates
$form['document']['title']
->validates( 'presence' )
->validates( 'length', array( 'minimum' => 3 ) );
$form['document']['body']
->validates( 'presence' );
Modul so sebou nesie niekoľko základných validátorov (presence, length,
format, email, url, custom, children,…), a je samozrejme možné si
dodefinovať vlastné. Mapa string → validátor je definovaná v triede
Validator\Registry. Ak si chcete pridať vlastný, stačí
vytvoriť funkciu a zaregistrovať ju:
$form['password'] = new Container;
$form['password']['first'] = new String;
$form['password']['second'] = new String;
function validatesPasswordConfirmation( $field, $options )
{
if( $field['first']->getValue() != $field['second']->getValue() )
{
$field->addError( 'Passwords do not match.' );
return false;
}
return true;
}
Validator\Registry::register( 'password_confirmation', 'validatesPasswordConfirmation' );
$form['password']->validates( 'password_confirmation' );
Registrovať sa dá aj lambda funkcia:
Validator\Registry::register( 'password_confirmation', function( $field, $options )
{
if( $field['first']->getValue() != $field['second']->getValue() )
{
$field->addError( 'Passwords do not match.' );
return false;
}
return true;
});
Ak potrebujete špeciálnu validáciu, možete použiť lambda funkciu priamo:
$form['password'] = new Container;
$form['password']['first'] = new String;
$form['password']['second'] = new String;
$form['password']->validates( function( $field )
{
if( $field['first']->getValue() != $field['second']->getValue() )
{
$field->addError( 'Passwords do not match.' );
return false;
}
return true;
});
Celý vtip je v tom, že si validačné pravidlá naviažete tam kde potrebujete. V tomto prípade zapúzdrujem obe heslá do jedného kontajneru a tým pádom si môžem zjednodušiť validáciu. Nerobí mi ale problém to urobiť aj bez kontajnéru.
Čo s tým?
Modul nie je zatiaľ verejný, keďže ho nemám plne nasadený a ustálený. Rád by som zistil, či sa aspoň niekomu zdá byť úžitočný. Mne to už teraz ušetrilo kopu nervov.



