Zapouzdření validátorů (nápady)
- Blizzy
- Člen | 149
Už dlouho mi vrtají hlavou validace formulářů a obecně dat v Nette.
Mám pocit, že současný koncept validací dat v Nette není dobře navržen z hlediska MVC ani OOP. Nelíbí se mi, že občas se blíží spíše procedurálnímu programování, také je příliš úzce spjatý s FormControl a není úplně dokonale rozšiřitelný.
Mnohem lepší by bylo, kdyby veškeré validace byli zapouzdřené a implementující určitý interface (např. IValidator nebo IValidation), který by zajišťoval jednotné rozhraní např. pro připojení k formulářům nebo všeobecné použití. Nejednalo by se o přímé validace objektů FormControl, ale o validace konkrétních dat. Od formulářů by měly být zcela oddělené. Validátory by vždy zpracovávali jen jeden druh dat (např. datum, konkrétní objekt, nebo nějaký druh řetězce).
Chyby při validaci by se měly také řešit více, než jen vrácení True/False, jestli je hodnota validní. Nevalidní hodnota (vrácení False) může nastat z více důvodů a validátor by měl na důvod upozornit nebo nějak reagovat (např. datum obsahuje úplně nesmyslné znaky nebo datum je mimo rozsah datového typu), nejsem si jistý, jestli by bylo dobré tohle řešit pomocí výjimek (různé ValidationExceptions) nebo raději pomocí nějaké vnitřní struktury chyb, protože nevalidní data v případě validátoru nejsou výjimečná.
Pokud jsou validátory zapouzdřené, je možné je použít i mimo formuláře, a také je možné, aby jeden validátor využíval jiný. Mělo by to také usnadnit sjednocení prvků validace v modelu a formulářích (tedy controlleru, vzhledem k tomu, že se jedná o zpracování dat od uživatele). V obou případech by se dal použít buď stejný validátor nebo by mohli oba validátory využívat stejný základ.
Ty validátory, které by měli svou JavaScriptovou obdobu by implementovali interface zajišťující zapojení do client scriptu (který už je součástí formulářů, a není oddělený), které může spočívat ve využití standardních validací v client scriptu (jako třeba regulární výraz), nebo využití AJAXového požadavku. Vzhledem k tomu, že client script je vyměnitelný, tohle může být problém, důležité je také rozhraní client scriptu.
Tyto moje nápady neberte příliš vážně, jedná se jen tak o nástin toho, co mě napadlo, ale myslím, že je potřeba se v této oblasti pohnout dál, a je podle mě důležité, aby se o tom diskutovalo. Pokud kohokoliv napadne, jaké by to mohlo mít komplikace, napište. Rád bych, kdyby ostatní napsali jejich nápady na řešení tohoto problému, rozvedli jednotlivé body, které jsem nastínil, nebo napsali proč jsou současné validace lepší.
- Patrik Votoček
- Člen | 2221
Už jsem o tom tady psal dokonce jsem to implementoval https://forum.nette.org/…tion-methods. Navíc teď se na to s odstupem času koukám tak že by se na to daly použít filtry http://cz.php.net/filters. něco málo je o tom tady http://zdrojak.root.cz/…r-konec-get/
- Roman Ožana
- Člen | 52
Jsem taky pro oddělení validátorů od Nette/Forms, protože pokud potřebujete validovat jiný, než uživatelský vstup (např. ze souboru) musíte si některé validátory zbytečně znova napsat.
S použitím PHP Filters bych neměl problém. Proč nevyužít něco, co už existuje.
- westrem
- Člen | 398
Rozhodne som pro, mmtalne prechadzam na Nette, predtym som pouzival vlastny framework, kde som mal validacnu a sanitacnu triedu, ktora zoskupovala validacne funkcie a teda sa dala pouzivat hocikde. Moja implementacia formularov s nou interne spolupracovala, no tak isto sa dala vyuzit pri praci s DB alebo na inych miestach. Chcelo by to uz len dotiahnut a zrealizovat objektovy navrh, to co navrhol Blizzy znie ale rozumne.
- David Grudl
- Nette Core | 8218
Tohle je dlouhodobě otevřené téma, protože je trošku komplexnější, než se na první pohled může zdát. A zároveň mám pocit, že je do jisté míry přeceňované. Zkuste totiž odpovědět na otázku, které z těch formulářových validátorů vám mimo formuláře skutečné chybí:
FormControl
- validateEqual
- validateFilled
- validateValid
- samotný proces definovaný pomocí addRule & addCondition
FileUpload
- validateFileSize
- validateMimeType
- validateImage
SelectBox, RadioList
- validateFilled
SubmitButton
- validateSubmitted
TextBase
- validateMinLength
- validateMaxLength
- validateLength
- validateEmail
- validateUrl
- validateRegexp
- validateInteger
- validateFloat
- validateRange
Umím si představit potřebu validace mimetype/image (což by znamenalo
spíše vyčlenit HttpUploadedFile::getContentType
) a emailové
adresy. Svým způsobem se může hodit i nějaké is_filled() kvůli zkur***
chování PHP "0" == false
. Ale je potřeba validovat i něco
dalšího?
Musím zdůraznit rozdílnou povahu validátorů pro uživatelský a
neuživatelský vstup. Například formulářový validátor URL by měl
akceptovat WWW.EXAMPLE.COM
i http://www.example.com
a
ideálně jej normalizovat, zatímco interní validační funkce by měla být
velmi přísná.
- David Grudl
- Nette Core | 8218
Vyki napsal(a):
Z té skupiny FileUpload a TextBase by se mi v modelu hodily všechny.
Stále však nerozumím té motivaci, proč místo
if (filesize($file) <= $size) ....
psát
if (Nette\Validators::isMaxFileSize($file $size)) ...
vrtak-cz napsal(a):
Hodí se mě minimálně všechno z
TextBase
ad validateMinLength, validateMaxLength, validateLength, validateRegexp: proč preferuješ druhý zápis před prvním?
if (String::length($s) < $len) ...
vs.
if (Nette\Validators::isMinLength($s, $len)) ...
ad validateInteger vs. validateFloat: DTTO
if (is_int($val)) ... // nebo is_numeric() nebo is_float()
vs.
if (Nette\Validators::isInteger($val)) ...
ad validateRange: DTTO
if ($val < $min || $val > $max) ...
vs.
if (!Nette\Validators::isInRange($val, $min, $max)) ...
ad validateUrl: konkrétní příklad použití?
- Vyki
- Člen | 388
David Grudl napsal(a):
Vyki napsal(a):
Z té skupiny FileUpload a TextBase by se mi v modelu hodily všechny.
Stále však nerozumím té motivaci, proč místo
if (filesize($file) <= $size) ....
psát
if (Nette\Validators::isMaxFileSize($file $size)) ...
Protože je to na jednom místě a bude to dobré i v napovídání IDE.
Zrovna filesize($file) <= $size
by samozřejmě šlo lednoduše
napsat takto, ale validování mime type už
na řádek není.
- Patrik Votoček
- Člen | 2221
David Grudl napsal(a):
vrtak-cz napsal(a):
Hodí se mě minimálně všechno z
TextBase
ad validateMinLength, validateMaxLength, validateLength, validateRegexp: proč preferuješ druhý zápis před prvním?
if (String::length($s) < $len) ... vs. if (Nette\Validators::isMinLength($s, $len)) ...
ad validateInteger vs. validateFloat: DTTO
if (is_int($val)) ... // nebo is_numeric() nebo is_float() vs. if (Nette\Validators::isInteger($val)) ...
ad validateRange: DTTO
if ($val < $min || $val > $max) ... vs. if (!Nette\Validators::isInRange($val, $min, $max)) ...
ad validateUrl: konkrétní příklad použití?
Protože to můžu nastavit jako callback.
- David Grudl
- Nette Core | 8218
Vyki napsal(a):
Protože je to na jednom místě a bude to dobré i v napovídání IDE. Zrovna
filesize($file) <= $size
by samozřejmě šlo lednoduše napsat takto, ale validování mime type už na řádek není.
Validování mimetype a emailu jsem speciálně uváděl jako případ, kde si dovedu vytvoření funkce představit. Takže se bavíme o všech ostatních případech.
- David Grudl
- Nette Core | 8218
vrtak-cz napsal(a):
Protože to můžu nastavit jako callback.
Příklad? Např. pro Nette\Validators::isInRange
- Vyki
- Člen | 388
Když budu validovat v modelu je určitě dobré když email a url budu validovat stejným regexpem jako se to dělá ve formulářích, a určitě je jednodušší volat funkci než hledat a opisovat regexp. Souhlasím, že validaci emailu, url by šlo zařadit do třídy string. Nevím, ale kam potom zařadit tu validaci mimetype, do Tools? Přišlo by mi dobré kdyby to bylo všechno na jednom místě a sjednocené.
- Patrik Votoček
- Člen | 2221
David Grudl napsal(a):
vrtak-cz napsal(a):
Protože to můžu nastavit jako callback.
Příklad? Např. pro Nette\Validators::isInRange
class Foo extends \Nette\Object
{
/**
* @validator(isInRange, 2, 10)
* @var int
*/
protected $foo;
//...
final protected function validate()
{
foreach (\Nette\Reflection\ClassReflection::from(get_called_class())->getProperties() as $property) {
if ($property->hasAnnotation('validator')) {
foreach ($property->getAnnotations() as $type => $validators) {
if ($type == 'validator') {
$validators = (array) $validators;
foreach ($validators as $validator) {
$tmp = callback("Nette\Validators::".$validator[0]);
$validator[0] = $this->foo;
if (!$tmp->invokeArgs((array) $validator))
throw new \InvalidArgumentException("Property {$property->name} is not valid");
}
}
}
}
}
}
}
BTW OT: vypadá to tak strašně protože nejde něco
jako ->getAnnotations('validator')
- srigi
- Nette Blogger | 558
Ola napsal(a):
Pokud by existoval způsob jak propojit/nabindovat validace v modelu s validací políček formuláře, pak budou validace pouze na jednom místě.
Ked som na Ostravskej PS prednasal ako to maju urobene inde (rozumej spolocnu validaciu Modelu o formularov), vsetci tam ohrnovali nos. Tak potom o com je #dpc tato tema?
Sry za hruby vyraz v predoslej vete. BTW David mi potom za barom vysvetloval, ze nekce vo frameworku triedu pre Model, kym sa nepride na to ako to s tym Modelom urobit spravne (cca ako bolo na phpguru.cz).
Kazdopadne, ak uz vazne nejako kcete poriesit tie validacie vo forme aj v Modeli, musi sa ten Model dopisat. Inak IMO nema zmysel o tom diskutovat.