Multiplier

Nástroj na dynamickou tvorbu komponent

Vyjděme od typického příkladu: mějme seznam zboží v eshopu, přičemž u každého budeme chtít vypsat formulář pro přidání zboží do košíku. Jednou z možných variant je obalit celý výpis do jednoho formuláře. Mnohem pohodlnější způsob nám však nabízí Nette\Application\UI\Multiplier.

Multiplier umožňuje pohodlně definovat továrničku pro více komponent. Funguje na principu vnořených komponent – každá komponenta dědící od Nette\ComponentModel\Container může obsahovat další komponenty.

Viz kapitola o komponentovém modelu v dokumentaci či přednáška od Honzy Tvrdíka.

Podstatou Multiplieru je, že vystupuje v pozici rodiče, který si své potomky dokáže vytvářet dynamicky pomocí callbacku předaného v konstruktoru. Viz příklad:

protected function createComponentShopForm(): Multiplier
{
    return new Multiplier(function () {
        $form = new Nette\Application\UI\Form;
        $form->addText('count', 'Počet zboží:')
            ->addRule($form::FILLED)
            ->addRule($form::INTEGER);
        $form->addSubmit('send', 'Přidat do košíku');
        return $form;
    });
}

Nyní můžeme v šabloně jednoduše u každého zboží nechat vykreslit formulář – a každý bude skutečně unikátní komponentou.

{foreach $items as $item}
    <h2>{$item->title}</h2>
    {$item->description}

    {control shopForm-$item->id}
{/foreach}

Argument předaný makru {control} je ve formátu, který říká:

  1. získej komponentu shopForm
  2. a z ní získej potomka $item->id

Při prvním volání bodu 1. shopForm ještě neexistuje, takže se zavolá jeho továrna createComponentShopForm. Na získané komponentě (instanci Multiplieru) je pak zavolána továrna konkrétního formuláře – což je anonymní funkce, kterou jsme Multiplieru v konstruktoru předali.

V další iteraci foreache již metoda createComponentShopForm volána nebude (komponenta existuje), ale protože hledáme jejího jiného potomka ($item->id bude v každé iteraci jiné), znovu bude zavolána anonymní funkce a vrátí nám nový formulář.

Jediné, co zbývá, je zajistit, aby nám formulář do košíku přidal skutečně to zboží, které má – aktuálně je formulář u každého zboží úplně totožný. Pomůže nám vlastnost Multiplieru (a obecně každé továrny na komponentu v Nette Frameworku), a to ta, že každá továrna jako svůj první argument dostává název tvořené komponenty. V našem případě to bude $item->id, což je přesně ten údaj, který potřebujeme. Stačí tedy lehce upravit tvorbu formuláře:

protected function createComponentShopForm(): Multiplier
{
    return new Multiplier(function ($itemId) {
        $form = new Nette\Application\UI\Form;
        $form->addText('count', 'Počet zboží:')
            ->addRule($form::FILLED)
            ->addRule($form::INTEGER);
        $form->addHidden('itemId', $itemId);
        $form->addSubmit('send', 'Přidat do košíku');
        return $form;
    });
}