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} タグに渡した引数は、次の意味を持つ形式に従っています。
- コンポーネント
shopFormを取得する。 - そこから
$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;
});
}