Multiplicateur : Composants dynamiques

Un outil pour la création dynamique de composants interactifs

Commençons par un problème typique : nous avons une liste de produits sur un site de commerce électronique et nous voulons accompagner chaque produit d'un formulaire add to cart. L'une des solutions consiste à regrouper toute la liste dans un seul formulaire. Un moyen plus pratique est d'utiliser Nette\Application\UI\Multiplier.

Multiplier vous permet de définir une usine pour plusieurs composants. Il est basé sur le principe des composants imbriqués – chaque composant héritant de Nette\ComponentModel\Container peut contenir d'autres composants.

Voir le modèle de composant dans la documentation.

Multiplier se pose comme un composant parent qui peut créer dynamiquement ses enfants en utilisant le callback passé dans le constructeur. Voir l'exemple :

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function () {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Amount:')
			->setRequired();
		$form->addSubmit('send', 'Add to cart');
		return $form;
	});
}

Dans le modèle, nous pouvons rendre un formulaire pour chaque produit – et chaque formulaire sera effectivement un composant unique.

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

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

L'argument passé à la balise {control} dit :

  1. obtenir un composant shopForm
  2. et retourne son enfant $item->id

Lors du premier appel de 1. le composant shopForm n'existe pas encore, la méthode createComponentShopForm est donc appelée pour le créer. Une fonction anonyme passée en paramètre à Multiplier, est ensuite appelée et un formulaire est créé.

Dans les itérations suivantes de foreach, la méthode createComponentShopForm n'est plus appelée car le composant existe déjà. Mais comme nous faisons référence à un autre enfant ($item->id varie entre les itérations), une fonction anonyme est à nouveau appelée et un nouveau formulaire est créé.

La dernière chose à faire est de s'assurer que le formulaire ajoute effectivement le bon produit au panier car dans l'état actuel, tous les formulaires sont identiques et nous ne pouvons pas distinguer à quels produits ils appartiennent. Pour cela nous pouvons utiliser la propriété de Multiplier (et en général de toute méthode de fabrique de composant dans Nette Framework) que chaque méthode de fabrique de composant reçoit le nom du composant créé comme premier argument. Dans notre cas, ce serait $item->id, ce qui est exactement ce dont nous avons besoin pour distinguer les produits individuels. Tout ce que vous avez à faire est de modifier le code de création du formulaire :

protected function createComponentShopForm(): Multiplier
{
	return new Multiplier(function ($itemId) {
		$form = new Nette\Application\UI\Form;
		$form->addInteger('amount', 'Amount:')
			->setRequired();
		$form->addHidden('itemId', $itemId);
		$form->addSubmit('send', 'Add to cart');
		return $form;
	});
}
version: 4.0