iterateWhile}について、あなたがいつも知りたかったことすべて。
{iterateWhile}
というタグは、foreach
サイクルでの様々なトリックに適しています。
次のようなデータベースのテーブルがあり、項目がカテゴリに分けられているとします。
id | catId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
もちろん、foreachループのアイテムをリストとして描画するのは簡単です。
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
しかし、各カテゴリを別々のリストで表示したい場合はどうすればよいのだろうか?言い換えれば、foreachサイクルで線形リストからアイテムをグループ化するタスクをどのように解決するかということです。出力はこのようになるはずだ。
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
このタスクをiterateWhileでいかに簡単かつエレガントに解決できるかをお見せします。
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
{foreach}
がサイクルの外側、つまり各カテゴリのリストの描画を示すのに対して、{iterateWhile}
タグは内側、つまり個々の項目を示しています。
終了タグの条件は、現在の要素と次の要素が同じカテゴリに属している限り、繰り返しを続けるというものです($iterator->nextValue
は次のアイテム)。
もし、この条件が常に満たされるなら、すべての要素が内部のサイクルで描かれることになる。
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
結果は次のようになる。
<ul>
<li>Apple</li>
<li>Banana</li>
<li>PHP</li>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
このような iterateWhile
の使い方にどんな利点があるのでしょうか?このチュートリアルの一番最初に紹介した解決策とどう違うのでしょうか?違いは、テーブルが空で要素を含んでいない場合、レンダリングが空にならないことです。
<ul></ul>
.
{iterateWhile}
のない場合の解決策
もし、同じタスクを完全に基本的な構造のテンプレートシステム、例えばTwigやBlade、あるいは純粋なPHPで解決するとしたら、解決策は次のようになります。
{var $prevCatId = null}
{foreach $items as $item}
{if $item->catId !== $prevCatId}
{* the category has changed *}
{* we close the previous <ul>, if it is not the first item *}
{if $prevCatId !== null}
</ul>
{/if}
{* we will open a new list *}
<ul>
{do $prevCatId = $item->catId}
{/if}
<li>{$item->name}</li>
{/foreach}
{if $prevCatId !== null}
{* we close the last list *}
</ul>
{/if}
しかし、このコードは理解しがたく、直感的ではありません。HTMLタグの開始と終了の間の接続がまったく明確ではありません。間違いがあっても一目瞭然ではありません。しかも、$prevCatId
のような補助変数を必要とする。
これに対して、{iterateWhile}
を使った解決策は、すっきりしていて、明確で、補助変数が必要なく、間違いがありません。
終了タグの条件
開始タグ{iterateWhile}
で条件を指定すると、動作が変わります。条件
(と次の要素への移動) は内部サイクルの最初で実行され、最後では実行されなくなります。
したがって、条件なしの{iterateWhile}
は常に入力されますが、{iterateWhile $cond}
は条件$cond
が満たされたときだけ入力されます。同時に、次の要素が$item
に書き込まれる。
これは,たとえば,各カテゴリーの最初の要素を異なる方法でレンダリングしたいような場合に便利である.
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
元のコードを修正して、最初の項目を描画し、次に同じカテゴリから追加の項目を内部ループで描画することにしましょう{iterateWhile}
:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->catId === $iterator->nextValue->catId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
ネストされたループ
1つのサイクルで複数の内部ループを作成し、さらにそれらを入れ子にすることができる。この方法で、例えば、サブカテゴリーをグループ化することができる。
テーブルsubCatId
に別のカラムがあるとします。各カテゴリが別のカラムにあることに加えて、各サブカテゴリは別のカラムにあります。
<ul>
にあることに加え、各サブカテゴリーは別々の <ol>
:
{foreach $items as $item}
<ul>
{iterateWhile}
<ol>
{iterateWhile}
<li>{$item->name}
{/iterateWhile $item->subCatId === $iterator->nextValue->subCatId}
</ol>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
フィルタ|バッチ
線形項目のグループ化は、フィルタbatch
によっても提供され、一定の要素数を持つバッチに分けられます。
<ul>
{foreach ($items|batch:3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
これは,次のように iterateWhile で置き換えることができます.
<ul>
{foreach $items as $item}
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>