Zapouzdření validátorů (nápady)

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Blizzy
Člen | 149
+
0
-

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ší.

Majkl578
Moderator | 1364
+
0
-

Jsem rozhodně pro oddělení validátorů tak, aby nebyly pevně svázané s Nette\Forms a tudíž šly použít kdekoliv. Také mi to už déle vrtá hlavou…
Mmj, takový pokus už tu byl.

Patrik Votoček
Člen | 2221
+
0
-

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
+
0
-

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.

hrach
Člen | 1838
+
0
-

Vrtak: když se koukneš do changelogu php, tak se stale jen řeší nějakej bug filters, nechal bych to tak, jak to zatím máš ;-)

westrem
Člen | 398
+
0
-

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
+
0
-

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á.

Vyki
Člen | 388
+
0
-

Z té skupiny FileUpload a TextBase by se mi v modelu hodily všechny.

Patrik Votoček
Člen | 2221
+
0
-

Hodí se mě minimálně všechno z TextBase

Ola
Člen | 385
+
0
-

Až na submitted, equal a valid si dovedu představit všechny.

Editoval Ola (19. 7. 2010 18:58)

David Grudl
Nette Core | 8218
+
0
-

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í?

Ola
Člen | 385
+
0
-

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ě.

Peppy
Člen | 137
+
0
-

@David Grudl: Tak potom načo máme tieto funkcie?

Editoval Peppy (19. 7. 2010 19:13)

Vyki
Člen | 388
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

vrtak-cz napsal(a):

Protože to můžu nastavit jako callback.

Příklad? Např. pro Nette\Validators::isInRange

Vyki
Člen | 388
+
0
-

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
+
0
-

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
+
0
-

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.