Multiplier: динамические компоненты
Инструмент для динамического создания интерактивных компонентов.
Начнём с типичного примера: представьте список товаров в интернет-магазине, где для каждой позиции нужна форма “Добавить в корзину”. Один из возможных подходов – обернуть весь список в одну форму. Однако куда более удобный способ предлагает Nette\Application\UI\Multiplier.
Multiplier позволяет удобно определить фабрику сразу для нескольких компонентов. Он работает по принципу вложенных компонентов: любой компонент, наследующий от Nette\ComponentModel\Container, может содержать другие компоненты.
См. главу о компонентной модели в документации.
Суть Multiplier в том, что он выступает родителем, который может динамически создавать своих потомков с помощью callback, переданного в конструктор. Смотрите пример:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function () {
$form = new Nette\Application\UI\Form;
$form->addInteger('amount', 'Количество:')
->setRequired();
$form->addSubmit('send', 'Добавить в корзину');
return $form;
});
}
Теперь в шаблоне мы можем просто отрисовать форму для каждого товара, и каждая из них действительно будет отдельным компонентом.
{foreach $items as $item}
<h2>{$item->title}</h2>
{$item->description}
{control "shopForm-$item->id"}
{/foreach}
Аргумент, переданный в тег {control}, следует формату, который
означает:
- Получить компонент
shopForm. - Получить у него потомка с именем
$item->id.
При первом выполнении пункта 1 компонента shopForm ещё нет,
поэтому вызывается его фабрика createComponentShopForm. Затем у
полученного компонента (экземпляра Multiplier) вызывается фабрика
конкретной формы – та самая анонимная функция, которую мы передали в
конструктор Multiplier.
На следующей итерации цикла foreach метод createComponentShopForm уже не
вызовется (компонент существует). Однако, поскольку мы ищем другого
потомка (ведь $item->id на каждой итерации будет разным), анонимная
функция вызовется снова и вернёт новую форму.
Остаётся только позаботиться о том, чтобы форма добавляла в корзину
правильный товар – сейчас форма одинакова для всех товаров. Здесь нам
помогает особенность Multiplier (да и вообще любой фабрики компонентов в Nette
Framework): каждая фабрика получает первым аргументом имя создаваемого
компонента. Кроме того, фабрика Multiplier получает вторым аргументом сам
экземпляр Multiplier. В нашем случае первым аргументом будет $item->id,
а это ровно те сведения, которые нам нужны. Значит, достаточно немного
изменить создание формы:
protected function createComponentShopForm(): Multiplier
{
return new Multiplier(function (string $itemId) {
$form = new Nette\Application\UI\Form;
$form->addInteger('amount', 'Количество:')
->setRequired();
$form->addHidden('itemId', $itemId);
$form->addSubmit('send', 'Добавить в корзину');
return $form;
});
}