グループ化について知りたかったことすべて
テンプレートでデータを扱う際、特定の基準に従ってデータをグループ化したり、特別に表示したりする必要性にしばしば直面します。Latte はこの目的のために、いくつかの強力なツールを提供しています。
フィルタと関数 |group
は、指定された基準に従ってデータを効率的にグループ化することを可能にし、フィルタ
|batch
はデータを固定サイズのバッチに分割することを容易にし、タグ
{iterateWhile}
は条件付きでループの進行をより複雑に制御する可能性を提供します。
これらの各タグは、データを扱うための特定の可能性を提供し、Latte
テンプレートで情報を動的かつ構造化して表示するための不可欠なツールになります。
フィルタと関数 group
カテゴリに分割された項目を持つデータベーステーブル items
を想像してください:
id | categoryId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Latte テンプレートを使用したすべての項目の簡単なリストは次のようになります:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
しかし、項目をカテゴリ別にグループ化して表示したい場合は、各カテゴリが独自のリストを持つように分割する必要があります。結果は次のようになります:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
このタスクは |group
を使用して簡単かつエレガントに解決できます。パラメータとして categoryId
を指定します。これは、項目が $item->categoryId
の値に基づいて小さな配列に分割されることを意味します($item
が配列の場合、$item['categoryId']
が使用されます):
{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
Latte ではフィルタを関数としても使用できるため、代替構文
{foreach group($items, categoryId) ...}
が得られます。
より複雑な基準で項目をグループ化したい場合は、フィルタのパラメータに関数を使用できます。例えば、名前の長さで項目をグループ化すると次のようになります:
{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
...
{/foreach}
$categoryItems
は通常の配列ではなく、イテレータのように動作するオブジェクトであることに注意することが重要です。グループの最初の項目にアクセスするには、first()
関数を使用できます。
このデータのグループ化における柔軟性により、group
は Latte
テンプレートでデータを表示するための非常に便利なツールになります。
ネストされたループ
個々の項目のサブカテゴリを定義する別の列 subcategoryId
を持つデータベーステーブルがあるとします。各メインカテゴリを個別の <ul>
リストに表示し、各サブカテゴリを個別のネストされた <ol>
リストに表示したいとします:
{foreach ($items|group: categoryId) as $categoryItems}
<ul>
{foreach ($categoryItems|group: subcategoryId) as $subcategoryItems}
<ol>
{foreach $subcategoryItems as $item}
<li>{$item->name}
{/foreach}
</ol>
{/foreach}
</ul>
{/foreach}
Nette Database との連携
Nette Database
と組み合わせてデータのグループ化を効果的に活用する方法を示しましょう。冒頭の例の
items
テーブルを扱っていると仮定します。これは、列 categoryId
を介して次の
categories
テーブルに接続されています:
categoryId | name |
---|---|
1 | Fruits |
2 | Languages |
3 | Colors |
items
テーブルからのデータは、Nette Database Explorer コマンド
$items = $db->table('items')
を使用して読み込みます。これらのデータを反復処理する間、$item->name
や
$item->categoryId
などの属性にアクセスできるだけでなく、categories
テーブルとの接続のおかげで、$item->category
を介して関連する行にもアクセスできます。この接続で興味深い使用法を示すことができます:
{foreach ($items|group: category) as $category => $categoryItems}
<h1>{$category->name}</h1>
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
この場合、|group
フィルタを使用して、列 categoryId
だけでなく、接続された行 $item->category
に基づいてグループ化します。これにより、キー変数に直接そのカテゴリの ActiveRow
が含まれるため、{$category->name}
を使用してその名前を直接出力できます。これは、グループ化がテンプレートをどのように明確にし、データの操作を容易にするかを示す実用的な例です。
フィルタ |batch
フィルタを使用すると、要素のリストを事前に決定された数の要素を持つグループに分割できます。このフィルタは、例えば、ページのより良い明瞭さや視覚的な配置のために、データを複数の小さなグループで表示したい場合に理想的です。
項目のリストがあり、それぞれ最大 3
つの項目を含むリストで表示したいとします。このような場合、|batch
フィルタの使用は非常に実用的です:
<ul>
{foreach ($items|batch: 3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
この例では、リスト $items
は小さなグループに分割され、各グループ($batch
)には最大 3
つの項目が含まれます。各グループはその後、個別の <ul>
リストに表示されます。
最後のグループに必要な数の要素が含まれていない場合、フィルタの 2 番目のパラメータを使用すると、このグループを何で補完するかを定義できます。これは、不完全な行が乱雑に見える可能性がある場合に、要素を美的に整列させるのに理想的です。
{foreach ($items|batch: 3, '—') as $batch}
...
{/foreach}
タグ {iterateWhile}
|group
フィルタで解決したのと同じタスクを、{iterateWhile}
タグを使用して示します。両方のアプローチの主な違いは、group
が最初に入力データ全体を処理してグループ化するのに対し、{iterateWhile}
は条件付きでループの進行を制御するため、反復が段階的に行われることです。
まず、iterateWhile を使用してカテゴリを持つテーブルを描画します:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
</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}
に条件を指定すると、動作が変わります:条件(および次の要素への移行)は、終了時ではなく、内側のループの開始時に実行されます。
したがって、条件なしの {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->categoryId === $iterator->nextValue->categoryId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
1 つのループ内で、複数の内側のループを作成し、それらをネストすることもできます。このようにして、サブカテゴリなどをグループ化できます。
テーブルに別の列 subcategoryId
があり、各カテゴリが個別の <ul>
にあるだけでなく、各サブカテゴリが個別の <ol>
にあるとします:
{foreach $items as $item}
<ul>
{iterateWhile}
<ol>
{iterateWhile}
<li>{$item->name}
{/iterateWhile $item->subcategoryId === $iterator->nextValue->subcategoryId}
</ol>
{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
</ul>
{/foreach}