Multiplier: динамічні компоненти

Інструмент для динамічного створення інтерактивних компонентів

Почнемо з типового прикладу: маємо список товарів в інтернет-магазині, причому біля кожного ми хочемо вивести форму для додавання товару в кошик. Одним з можливих варіантів є обгортання всього списку в одну форму. Набагато зручніший спосіб нам пропонує Nette\Application\UI\Multiplier.

Multiplier дозволяє зручно визначити фабрику для кількох компонентів. Він працює за принципом вкладених компонентів – кожен компонент, що успадковує від Nette\ComponentModel\Container, може містити інші компоненти.

Див. розділ про модель компонентів у документації або лекцію від Honza Tvrdík.

Суть Multiplier полягає в тому, що він виступає в ролі батька, який може динамічно створювати своїх нащадків за допомогою callback-функції, переданої в конструкторі. Див. приклад:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Кількість товару:')
			->setRequired();
		$form->addSubmit('send', 'Додати в кошик');
		return $form;
	});
}

Тепер ми можемо в шаблоні просто біля кожного товару відобразити форму – і кожна буде дійсно унікальним компонентом.

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

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

Аргумент, переданий у тезі {control}, має формат, який говорить:

  1. отримай компонент shopForm
  2. і з нього отримай нащадка $item->id

При першому виклику пункту 1. shopForm ще не існує, тому викликається його фабрика createComponentShopForm. На отриманому компоненті (екземплярі Multiplier) потім викликається фабрика конкретної форми – це анонімна функція, яку ми передали Multiplier у конструкторі.

У наступній ітерації foreach метод createComponentShopForm вже не буде викликаний (компонент існує), але оскільки ми шукаємо іншого його нащадка ($item->id буде різним у кожній ітерації), знову буде викликана анонімна функція і поверне нам нову форму.

Єдине, що залишається, – це забезпечити, щоб форма додала в кошик дійсно той товар, який потрібно – наразі форма біля кожного товару абсолютно однакова. Допоможе нам властивість Multiplier (і загалом кожної фабрики компонентів у Nette Framework), а саме те, що кожна фабрика як свій перший аргумент отримує назву створюваного компонента. У нашому випадку це буде $item->id, що є саме тим даними, які нам потрібні. Достатньо лише трохи змінити створення форми:

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function ($itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('count', 'Кількість товару:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Додати в кошик');
		return $form;
	});
}
версія: 4.0