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 2 - Tvorba formulářového frameworku v PHP - HtmlBuilder

V minulé lekci, Tvorba formulářového frameworku v PHP - Motivace, jsme si vysvětlili, že díky použití frameworku pro obsluhu formulářů budeme část aplikace programovat několikrát rychleji.

V dnešním dílu našeho tutoriálu k PHP formulářům si vytvoříme jednoduchou třídu pro renderování HTML kódu.

Motivace

MVC je sice hezká věc, ale v našich aplikacích budeme narážet i na situace, kdy potřebujeme často renderovat malé úseky HTML kódu nebo renderovat takový HTML kód, kde množství prezentační logiky překoná samotný objem HTML kódu. Použití šablon je zde možné, ale nemusí být vždy efektivní, proto budeme renderovat HTML přímo.

Udělejme si jednoduchý příklad. Budeme chtít vyrenderovat HTML kód pro <select>, který vypadá následovně:

<label for="znacka-vozu">Značka vozu</label>
<select name="znacka-vozu" id="znacka-vozu" required="required">
    <option value="skoda">Škoda</option>
    <option value="bmw">BMW</option>
    <option value="audi">Audi</option>
</select>

Chtěli bychom, aby se <select> vyrenderoval pomocí nějaké PHP metody. Zatím si metodu představme jako statickou na nějakém helperu, volali bychom ji takto:

<?= FormHelper::renderSelect('znacka-vozu', 'Značka vozu', true, array(
    'Škoda' => 'skoda',
    'BMW' => 'bmw',
    'Audi' => 'audi',
)) ?>

Že podobnou metodu pro práci s formuláři potřebujeme by mělo být jasné, jelikož pole možností často získáváme z databáze a se statickým HTML bychom si tedy nevystačili.

Implementace metody by mohla vypadat následovně:

<?php
public static function renderSelect(string $name, string $title, bool $required, array $data) : string
{
    $html = '<label for="' . htmlspecialchars($name) . '">' . htmlspecialchars($title) . '</label>';
    $html .= '<select name="' . htmlspecialchars($name) . '" id="' . htmlspecialchars($name) . '" ';
    if ($required)
        $html .= 'required="required"';
    $html .= '>';
    foreach ($data as $title => $value)
    {
        $html .= '<option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($title) . '</option>';
    }
    return $html . '</select>';
}

V kódu jsem zanedbal označení vybrané položky. Kód je začátečnický a trpí mnoha neduhy. HTML se špatně zvýrazňuje, protože je jako obyčejný string. Jsme odkázáni na ruční ošetřování hodnot pomocí PHP funkce htmlspecialchars() a vše znepřehledňuje konkatenace řetězce do proměnné $html.

HtmlBuilder

Třída HtmlBuilder vychází z principu SAX, který se používá pro zápis XML souborů. Odstiňuje nás od samotné HTML syntaxe a generování kódu zjednodušuje na práci s jednotlivými elementy. Vnitřně se HTML samozřejmě sestavuje podobně, jako jsme si to ukázali výše. Výhodou však je, že zvenčí s ním pracujeme jako s elementy a ne jako s řetězcem.

Ukažme si vyrenderování selectu pomocí třídy HtmlBuilder:

public static function renderSelect(string $name, string $title, bool $required, string $data) : string
{
    $builder = new HtmlBuilder();
    $builder->addValueElement('label', $title, array(
        'for' => $name,
    ));
    $selectAttributes = array(
        'name' => $name,
        'id' => $name,
    );
    if ($required)
        $selectAttributes['required'] = 'required';
    $builder->startElement('select', $selectAttributes);
    foreach ($data as $title => $value)
    {
        $builder->addValueElement('option', $title, array(
            'value' => $value,
        ));
    }
    $builder->endElement();
    return $builder->render();
}

Pracujeme pouze s elementy a poli jejich atributů. Vše se spojuje a ošetřuje samo, máme minimum možností něco zkazit. Výsledkem je totožný HTML kód, jako na začátku článku.

Implementace

Připravme si třídu HtmlBuilder a rovnou do ní vložme dva privátní atributy. Bude se jednat o sestavovaný HTML řetězec a zásobník otevřených párových elementů. Díky zásobníku víme, který element jsme naposledy otevřeli a můžeme ho tak jednoduše uzavřít bez uvedení jeho názvu:

class HtmlBuilder
{
    private string $html = '';
    private array $elementStack = array();

}

Třídu si samozřejmě jako vždy komentujte.

Přidejme privátní metodu k vyrenderování elementu:

private function renderElement(string $name, array $htmlParams, bool $pair) : void
{
    $this->html .= '<' . htmlspecialchars($name);
    foreach ($htmlParams as $key => $value)
    {
        $this->html .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
    }
    if (!$pair)
        $this->html .= ' /';
    $this->html .= '>';
    if ($pair)
        array_push($this->elementStack, $name);
}

Metodě předáme název elementu, jeho HTML parametry a upřesníme, zda je párový. Metoda vytvoří jeho HTML řetězec a připojí ho do výsledného HTML. Pokud je element párový, uložíme si do zásobníku také informaci, že je otevřený.

Pomocí této privátní metody vytvoříme několik veřejných:

public function addElement(string $name, array $htmlParams = array()) : void
{
    $this->renderElement($name, $htmlParams, false);
}

public function startElement(string $name, array $htmlParams = array()) : void
{
    $this->renderElement($name, $htmlParams, true);
}

public function addValue(string $value, bool $doNotEscape = false) : void
{
    $this->html .= $doNotEscape ? $value : htmlspecialchars($value);
}

public function endElement(string $name = '') : void
{
    if (empty($name))
        $name = array_pop($this->elementStack);
    $this->html .= '</' . htmlspecialchars($name) . '>';
}

function addValueElement(string $name, string $value, array $htmlParams = array(), bool $doNotEscape = false) : void
{
    $this->startElement($name, $htmlParams, true);
    $this->addValue($value, $doNotEscape);
    $this->endElement();
}

Představme si, co které metody dělají:

  • Metoda addElement() vyrendruje jednoduchý nepárový element,
  • startElement() otevře párový element,
  • addValue() přidá HTML kód - buď do otevřeného elementu, nebo klidně mimo něj, můžeme si také zvolit, zda se má hodnota převádět na entity, či nikoli,
  • endElement() uzavře poslední otevřený párový element, pokud bychom chtěli uzavírat nějaký element, který otevřela např. jiná instance builderu, můžeme uvést i jméno,
  • addValueElement() otevře párový element, vloží do něj hodnotu a poté ho uzavře.

Výsledné HTML vrátí metoda render():

public function render() : string
{
    return $this->html;
}

Máme hotovo. Okomentovaná třída je ke stažení v příloze.

V příští lekci, FormControl - Předek pro formulářové kontrolky v PHP, započneme práce na třídě FormControl, která bude předkem pro všechny kontrolky formuláře.


 

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

 

Předchozí článek
Tvorba formulářového frameworku v PHP - Motivace
Všechny články v sekci
Pokročilá obsluha formulářů v PHP
Přeskočit článek
(nedoporučujeme)
FormControl - Předek pro formulářové kontrolky v PHP
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
21 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