Multiplier: 動的なコンポーネント

インタラクティブなコンポーネントを動的に作るための道具です。

典型的な例から始めましょう。ネットショップの商品一覧で、各商品に「カートに追加」のフォームを付けたいとします。ひとつのやり方は、一覧全体をひとつのフォームで包むことです。しかし Nette\Application\UI\Multiplierは、はるかに便利な方法を提供します。

Multiplier を使うと、複数のコンポーネントのファクトリを手軽に定義できます。原理は入れ子のコンポーネントで、Nette\ComponentModel\Containerを継承したコンポーネントは、ほかのコンポーネントを含められます。

ドキュメントのコンポーネントモデルの章をご覧ください。

Multiplier の本質は、コンストラクタに渡されたコールバックを使って子を動的に作れる親として働くことです。例を見てみましょう。

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} タグに渡した引数は、次の意味を持つ形式に従っています。

  1. コンポーネント shopForm を取得する。
  2. そこから $item->id という名前の子を取得する。

1 の最初の呼び出しでは shopForm コンポーネントがまだ存在しないので、そのファクトリ createComponentShopForm が呼ばれます。次に、得られたコンポーネント(Multiplier のインスタンス)に対して、個別のフォームのファクトリが呼ばれます。それが Multiplier のコンストラクタに渡した無名関数です。

foreach ループの次の反復では、createComponentShopForm メソッドはもう呼ばれません(コンポーネントがすでに存在するからです)。しかし探している子が違う(反復ごとに $item->id が違う)ので、無名関数がもう一度呼ばれ、新しいフォームを返します。

あとはフォームが正しい商品をカートに追加するようにするだけです。今のところフォームはどの商品でも同じだからです。ここで Multiplier の(そして一般に Nette Framework のどのコンポーネントファクトリの)性質が役立ちます。どのファクトリも、作られるコンポーネントの名前を第 1 引数として受け取ります。さらに Multiplier のファクトリは、第 2 引数として Multiplier のインスタンス自身を受け取ります。この場合、第 1 引数は $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;
	});
}
バージョン: 4.x