Custom validace
- mkrause
- Člen | 20
Ahoj Davide,
zdá se mi, že custom validace vstupů z formuláře není momentálně to pravé ořechové. Pokud chci přidat vlastní validační pravidlo, musím zřejmě udělat jednu z následujících věcí:
- vytvořit si potomka patřičného formulářového pole a přepsat jeho metodu validate
- ve zpracování formuláře po zavolání isValid provést vlastní validaci, nasetovat případné chyby pomocí metody addError a pak se vrátit zpět do té větve zpracování, kde se formulář považuje za nevalidní
- vytvořit potomka třídy Form, která umožní po isSubmitted provést vlastní validaci, pak při volání isValid nepřepíše $errors prázdným polem a nakonec nastaví příznak valid i s ohledem na výsledky custom validace
Ani jedno mi nepřipadá moc elegantní. První varianta předpokládá, že formulářové pole bude mít přístup ke všem věcem potřebným k validaci (například databázová konexe), což vede k nežádoucím vazbám. Kromě toho IMHO není z hlediska designu hezké, když kvůli validaci musím vytvořit nový typ formulářového pole, které se od svého předka jinak vůbec neliší, a navíc tím poněkud utrpí původní vize, že validátory jsou zbytečný overhead (uživateli je celkem fuk, jestli musí napsat validátor nebo potomka jiné třídy).
Druhá varianta vede k menší přehlednosti hlavního kódu okolo zpracování formuláře (formulář je přes isValid prohlášen za validní a pak posléze znevalidněn).
Třetí varianta je asi použitelná, ale spíš tak nějak z nouze – psát potomka formuláře kvůli validaci jeho polí také není nic moc.
Co si o tom myslíš?
- David Grudl
- Nette Core | 8228
Podporu custom validace jsem implementoval do „nových“ formulářů. Jde
o rozšíření současného zápisu pravidel
$control->addRule(Form::FILLED, 'Message')
tak, že místo
konstanty je možné uvést vlastní callback:
class MyValidators
{
public static function oddNumber($control /*, $arg]*/)
{
// pokud je podmínka splněna, metoda vrací TRUE; jinak FALSE
return (bool) $control->value % 2;
}
}
$form = new Form();
$form->addText('field', 'Odd number:')
->addRule('MyValidators::oddNumber', 'The entered number is not odd.');
Lze použít i pseudotyp callback
addRule(array($obj, 'method') ...)
nebo v PHP 5.3 novou
syntax:
$form = new Form();
$form->addText('number', 'Číslo:')
->addRule(
function ($control) { return (bool) $control->value % 2; },
'Zadané číslo není dostatečně liché'
);
Pokud se však callback zapíše jako řetězec (tedy první příklad), má to výhodu v tom, že je možné ho předat i klientské straně a provést validaci v JavaScriptu.
- mkrause
- Člen | 20
Pokud se však callback zapíše jako řetězec (tedy první příklad), má to výhodu v tom, že je možné ho předat i klientské straně a provést validaci v JavaScriptu
Prošel jsem si související třídy (Nette/Forms/JavaScript) a tuhle možnost jsem v tom nějak nenašel. Díval jsem se špatně nebo tam ještě něco chybí? Existuje ekvivalent dřívějšího $form->rules->addScript()? Používali jsme to právě pro custom validaci v klientovi a také jako hack k tomu, aby se s formulářem po úspěšné validaci provedly některé speciální akce namísto normálního submitu. Jak by se tohle mělo ideálně řešit teď?
- David Grudl
- Nette Core | 8228
mkrause napsal(a):
Prošel jsem si související třídy (Nette/Forms/JavaScript) a tuhle možnost jsem v tom nějak nenašel. Díval jsem se špatně nebo tam ještě něco chybí?
Ukázkou, jak by to mohlo vypadat, je třída UserClientScript. (V současné době je do třídy Form natvrdo zadrátována třída InstantClientScript, která generuje JavaScript stejný od prvních verzí formulářů.)
Existuje ekvivalent dřívějšího $form->rules->addScript()?
Ano, dá se to udělat takto
addRule(Form::SCRIPT, NULL, $script)
. Nebo přes extension
method:
// lze od PHP 5.3
use Nette\Forms\Rules;
use Nette\Forms\Form;
Rules::extensionMethod('addScript', function(Rules $thisObj, $script)
{
return $thisObj->addRule(Form::SCRIPT, NULL, $script);
});
// PHP 5.2
function Rules_prototype_addScript(Rules $thisObj, $script)
{
return $thisObj->addRule(Forms::SCRIPT, NULL, $script);
}
a pak bude fungovat addScript jako dřív.
Neříkám, že je to ideální, podpora pro JavaScript není ve finálním stádiu.
- Bohouš
- Člen | 4
Čau, zkouším si udělat vlastní validátor na unikátnost uživatelského jména, pravidlo přidávám taklhle:
<?php
(...)
$form->addText('username','Uživatelské jméno')
->addRule(array('Users_Users','userExists'),'msg');
(...)
?>
a třída Users_Users vypadá takhle:
<?php
class Users_Users extends DibiTable
{
protected $name = 's_uzivatele'; // nejsem si jisty, jestli se to takhle v dibi pise
public function userExists($control)
{
if(!$row = $this->fetch(array('username'=>$control->control->value)))
return true;
return false;
}
}
?>
nastavení databáze:
database.host = "localhost"
database.driver = "mysqli"
database.username = "***"
database.database = "***"
database.password = "***"
database.charset = "utf8"
Co chci udělat je asi jasné, ale když formulář odešlu, tak mi spadne apache :-) Co dělám špatně?
Že to nemá s nette a dibi nic společného si říkám taky, ale bez problémů mi tu beží několik webů na Zendu + samozřejmě další části webu, ke kterému ta registrace patří.
- David Grudl
- Nette Core | 8228
Určitě by tam mělo být místo $control->control->value
jen $control->value
.
Dále pokud v pravidlu máš statickou metodu
array('Users_Users','userExists')
, musí být i userExists
statická a nemůže používat $this. Nebo jako callback definovat nestatickou
metodu array($nejakyObj,'userExists')
- Ola
- Člen | 385
David Grudl napsal(a):
Určitě by tam mělo být místo
$control->control->value
jen$control->value
.Dále pokud v pravidlu máš statickou metodu
array('Users_Users','userExists')
, musí být i userExists statická a nemůže používat $this. Nebo jako callback definovat nestatickou metoduarray($nejakyObj,'userExists')
Zdravím, řeším něco podobnýho jako uživatel Bohouš a mám s tím pár problémů..
- Když použiju v addRule array jakožto první parametr, vyskočí mi Notice o tom že používám místo stringu array .. jde to nějak vyřešit? Používám nejnovější revizi..
- Jde u toho příkladu co posílal Bohouš nějak vyřešit to používání
this kromě použití callbacku
array($nejakyObj, 'userExists')
? Konkrétně mě zlobí proměnná $this, zkoušel jsem jí nahradit parent:: ale stejně potom aplikace křičí že je někde na řádku 278 v DibiTable.php použitá proměnná $this a aplikaci to zastaví..
btw. v DibiTable.php je to tenhle řádek:
return $this->complete($this->connection->query(
Díky za rady..
Editoval Ola (29. 10. 2008 17:18)