Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 447x (2.83 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
FormControl - Předek pro formulářové kontrolky v PHP
Všechny články v sekci
Pokročilá obsluha formulářů v PHP
Přeskočit článek
(nedoporučujeme)
Formulářový framework v PHP - InputBox
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
15 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity