Lekce 4 - Dokončení třídy FormControl v PHP
V minulé lekci, FormControl - Předek pro formulářové kontrolky v PHP, jsme si započali práce na třídě
FormControl
, která je předkem pro všechny formulářové
kontrolky.
Dnes budeme v PHP tutoriálu pokračovat tvorbou abstraktní třídy
FormControl
. Přidáme do ní další metody, které nastaví
pravidla pro ověřování formulářů, jako je minimální délka pole,
pravidlo pro validaci hesla, data a času, či povinnost připojit souboru.
Metody opět budou poskytovat validaci jak na straně serveru, tak i na straně
klienta.
Pravidlo minimální délky
Protože jsme si přidali metodu addPatternRule()
, můžeme
nyní v dalších pravidlech ověřovat regulární výrazy. To využijeme
např. u pravidla pro minimální délku hodnoty:
public function addMinLengthRule(int $minLength, bool $validateClient = true, bool $validateServer = true) : FormControl { return $this->addPatternRule('.{' . $minLength . ',}', $validateClient, $validateServer); }
Pravidlo hesla
Dále přidáme pravidlo pro validaci hesla. Heslo bude muset být delší než 6 znaků, což vyřešíme přidáním pravidla s patternem (vzorem):
public function addPasswordRule(bool $validateClient = true, bool $validateServer = true) { $this->addMinLengthRule(6, $validateClient); return $this->addRule(array( 'type' => Rule::Password, 'message' => 'Heslo nesmí začínat ani končit mezerou a musí být dlouhé alespoň 6 znaků.', ), $validateClient, $validateServer); }
Zadané heslo nesmí začínat ani končit mezerou, což ověříme pro zjednodušení až na serveru.
Pravidlo pro datum a čas
Nakonec přidejme trojici metod pro přidání pravidla na datum a čas, samotné datum a samotný čas. Pro každé pravidlo přidáme opět pattern:
public function addDateTimeRule(bool $validateClient = true, bool $validateServer = true) : FormControl { $this->addPatternRule('[0-3]?[0-9]\.[0-1]?[0-9]\.[0-9]{4}\s[0-2]?[0-9]\:[0-5]?[0-9](\:[0-5]?[0-9])?'); return $this->addRule(array( 'type' => Rule::DateTime, 'format' => DateUtils::DATETIME_FORMAT, 'message' => 'Hodnota musí být ve formátu: dd.mm.yyyy hh:mm(:ss)', ), $validateClient, $validateServer); } public function addDateRule(bool $validateClient = true, bool $validateServer = true) : FormControl { $this->addPatternRule('[0-3]?[0-9]\.[0-1]?[0-9]\.[0-9]{4}'); return $this->addRule(array( 'type' => Rule::DateTime, 'format' => DateUtils::DATE_FORMAT, 'message' => 'Hodnota musí být ve formátu: dd.mm.yyyy', ), $validateClient, $validateServer); } public function addTimeRule(bool $validateClient = true, bool $validateServer = true) : FormControl { $this->addPatternRule('[0-2]?[0-9]\:[0-5]?[0-9](\:[0-5]?[0-9])?'); return $this->addRule(array( 'type' => Rule::DateTime, 'format' => DateUtils::TIME_FORMAT, 'message' => 'Hodnota musí být ve formátu: hh:mm(:ss)', ), $validateClient, $validateServer); }
Povinný soubor
Metody zakončeme pravidlem pro nahrání povinného souboru:
public function addFileRequiredRule(bool $validateClient = true, bool $validateServer = true) : FormControl { return $this->addRule(array( 'type' => Rule::RequiredFile, 'message' => 'Soubor je povinný', ), $validateClient, $validateServer); }
Pravidel by se samozřejmě dalo vymyslet ještě spoustu, ale kombinací
stávajících jsme zatím docílili všeho, co jsme potřebovali. A kdyby něco
náhodou nestačilo, vždy se dají jednoduše přidat. Určitě bychom však
jejich počet měli držet na absolutním minimu, pravidla se stejným
mechanismem budeme z těchto ještě později odvozovat ve třídě
Form
.
Validace
Kontrolce tedy můžeme přidat několik pravidel. Nyní napíšeme tu část třídy, která bude jednotlivá pravidla ověřovat. Jak již bylo řečeno, budeme to dělat 2x, jednou na straně klienta a jednou na straně serveru.
Klientská část
Metoda níže přidá kontrolce HTML atributy podle validačních pravidel, které obsahuje. Budeme využívat atributů HTML 5, velmi jednoduše tak ověříme regulární výrazy, povinná pole a maximální délku:
public function addClientParams() : void { foreach ($this->rules as $rule) { if ($rule['validate_client']) { switch ($rule['type']) { case Rule::Required: case Rule::RequiredFile: $this->htmlParams['required'] = 'required'; break; case Rule::MaxLength: $this->htmlParams['maxlength'] = $rule['max_length']; break; case Rule::Pattern: if (!isset($this->htmlParams['pattern'])) $this->htmlParams['pattern'] = $rule['pattern']; break; } } } }
HTML5 bohužel neumí více patternů, a tak se bude ověřovat jen první. Pokud bude mít pole několik dalších (což je nepravděpodobné), ověří se až na serveru.
Serverová část
Podobný switch
umístíme i do metody checkRule()
,
která ověřuje jedno pravidlo na serveru. Všimněme si, že používáme
knihovny DateUtils
a StringUtils
, které jsme si
předtím vytvořili v kurzu PHP knihovny:
private function checkRule(array $rule) : bool { $name = $this->name; switch ($rule['type']) { case Rule::Required: return isset($_POST[$name]) && (is_numeric($_POST[$name]) || !empty($_POST[$name])); case Rule::MaxLength: return !isset($_POST[$name]) || !$_POST[$name] || mb_strlen($_POST[$name]) <= $rule['max_length']; case Rule::Pattern: return !isset($_POST[$name]) || !$_POST[$name] || preg_match('~^' . $rule['pattern'] . '$~u', $_POST[$name]); case Rule::RequiredFile: return isset($_FILES[$name]) && isset($_FILES[$name]['name']) && $_FILES[$name]['name']; case Rule::DateTime: return !isset($_POST[$name]) || !$_POST[$name] || DateUtils::validDate($_POST[$name], $rule['format']); case Rule::Password: return !isset($_POST[$name]) || !$_POST[$name] || ((StringUtils::removeAccents($_POST[$name]) == $_POST[$name]) && (mb_strlen($_POST[$name]) >= 6)); } return false; }
Pokud některé pravidlo selže, budeme chtít, aby se kontrolka podbarvila
červeně. K tomuto účelu třídě přidejme veřejný atribut
$invalid
:
public bool $invalid;
Validace zakončeme vrcholnou metodou checkValidity()
, která
ověří všechna pravidla, a pokud některé neplatí, nastaví vlastnost
invalid
na true
, tím kontrolce přidá CSS třídu
.invalid
a vyvolá výjimku:
public function checkValidity() : void { foreach ($this->rules as $rule) { if (($rule['validate_server']) && (!$this->checkRule($rule))) { $this->invalid = true; $this->addClass('invalid'); throw new UserException($rule['message']); } } }
Třídu UserException
budeme používat pro všechny výjimky,
jejichž zpráva je určená pro uživatele. Kód třídy je následující:
class UserException extends Exception { }
Rendering
Renderování (generování HTML kódu pro kontrolku) budeme realizovat
abstraktní metodou, kterou si každá konkrétní kontrolka implementuje po
svém. Zároveň však budeme chtít, aby se těsně po zavolání renderování
přidaly kontrolce klientské validační parametry. Z toho důvodu necháme
metodu potomka jako protected
a zavoláme ji z veřejné
render()
:
protected abstract function renderControl(bool $isPostBack); public function render(bool $validateClient, bool $isPostBack) { if ($validateClient) $this->addClientParams(); return $this->renderControl($isPostBack); }
Načtení a uložení dat
Zbývá ještě dodat nějaké rozhraní pro ukládání a načítání dat
z/do kontrolky. Budeme rovnou počítat s tím, že kontrolka může obsahovat
více hodnot a ne jen jednu. Můžeme tak realizovat např.
CheckList
, což je kontrolka, obsahující několik políček typu
CheckBox
. Výhodou více polí v jedné kontrolce je snadnější
tvorba formuláře.
Dodejme metodu getData()
, která vrátí data v kontrolce. Pokud
byla nějaká hodnota odeslaná na server (v superproměnné $_POST
je klíč s názvem kontrolky), tak vrátí tu, jinak nevrátí nic. Abychom
zachovali koncept vracení více hodnot, vrátíme vždy pole. Kontrolky s více
políčky si tuto metody potom přepíší, těm klasickým bude
postačovat:
public function getData() : array { return isset($_POST[$this->name]) ? array($this->name => $_POST[$this->name]) : array(); }
Podobně udělejme metodu, která vrací klíče všech polí v kontrolce:
public function getKeys() : array { return array($this->name); }
Metoda pro nastavení hodnoty bude již záviset na potomkovi, a proto ji jen předepíšeme jako abstraktní:
public abstract function setData(string $key, string $value);
Tím máme základ kontrolek hotový. Hotová a zdokumentovaná třída
FormControl
společně s výčtem validačních pravidel je ke
stažení v příloze.
Příště, v lekci Formulářový framework v PHP - InputBox, si napíšeme první kontrolku, kterou bude
InputBox
.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 453x (2.83 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP