Minden, amit valaha tudni akartál a csoportosításról
Amikor sablonokban adatokkal dolgozol, gyakran találkozhatsz azzal az igénnyel, hogy azokat csoportosítani vagy bizonyos kritériumok szerint specifikusan megjeleníteni kell. A Latte erre a célra több erős eszközt is kínál.
A |group
szűrő és függvény lehetővé teszi az adatok hatékony csoportosítását a megadott kritérium
szerint, a |batch
szűrő megkönnyíti az adatok fix méretű adagokra osztását, a {iterateWhile}
tag
pedig lehetőséget nyújt a ciklusok bonyolultabb vezérlésére feltételekkel. Mindegyik tag specifikus lehetőségeket kínál
az adatokkal való munkához, így nélkülözhetetlen eszközökké válnak az információk dinamikus és strukturált
megjelenítéséhez a Latte sablonokban.
A group
szűrő és függvény
Képzelj el egy items
adatbázistáblát, amely kategóriákba sorolt elemeket tartalmaz:
id | categoryId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Az összes elem egyszerű listája Latte sablonnal így nézne ki:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Ha azonban azt szeretnénk, hogy az elemek kategóriák szerint csoportosítva legyenek, úgy kell őket felosztani, hogy minden kategóriának saját listája legyen. Az eredménynek így kellene kinéznie:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
A feladat könnyen és elegánsan megoldható a |group
segítségével. Paraméterként a
categoryId
-t adjuk meg, ami azt jelenti, hogy az elemek kisebb tömbökre lesznek osztva a
$item->categoryId
értéke alapján (ha $item
tömb lenne, akkor $item['categoryId']
kerülne felhasználásra):
{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
A szűrőt Latte-ban függvényként is lehet használni, ami alternatív szintaxist ad nekünk:
{foreach group($items, categoryId) ...}
.
Ha bonyolultabb kritériumok szerint szeretnéd csoportosítani az elemeket, használhatsz függvényt a szűrő paraméterében. Például az elemek csoportosítása a név hossza szerint így nézne ki:
{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
...
{/foreach}
Fontos megjegyezni, hogy a $categoryItems
nem egy szokásos tömb, hanem egy objektum, amely iterátorként
viselkedik. A csoport első eleméhez való hozzáféréshez használhatod a first()
függvényt.
Ez a rugalmasság az adatok csoportosításában teszi a group
-ot kivételesen hasznos eszközzé az adatok Latte
sablonokban történő megjelenítéséhez.
Beágyazott ciklusok
Képzeljük el, hogy van egy adatbázistáblánk egy további subcategoryId
oszloppal, amely az egyes elemek
alkategóriáit definiálja. Szeretnénk minden fő kategóriát egy külön <ul>
listában, és minden
alkategóriát egy külön beágyazott <ol>
listában megjeleníteni:
{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}
Kapcsolat a Nette Database-zel
Nézzük meg, hogyan lehet hatékonyan kihasználni az adatok csoportosítását a Nette Database-zel kombinálva. Tegyük fel,
hogy az items
táblával dolgozunk a bevezető példából, amely a categoryId
oszlopon keresztül
kapcsolódik ehhez a categories
táblához:
categoryId | name |
---|---|
1 | Fruits |
2 | Languages |
3 | Colors |
Az items
tábla adatait a Nette Database Explorer segítségével olvassuk be a
$items = $db->table('items')
paranccsal. Ezen adatok feletti iteráció során nemcsak az olyan attribútumokhoz
férhetünk hozzá, mint a $item->name
és $item->categoryId
, hanem a categories
táblával való kapcsolatnak köszönhetően a kapcsolódó sorhoz is a $item->category
-n keresztül. Ezen a
kapcsolaton keresztül érdekes felhasználást demonstrálhatunk:
{foreach ($items|group: category) as $category => $categoryItems}
<h1>{$category->name}</h1>
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
Ebben az esetben a |group
szűrőt használjuk a kapcsolódó $item->category
sor szerinti
csoportosításhoz, nem csak a categoryId
oszlop szerint. Ennek köszönhetően a kulcs változóban közvetlenül az
adott kategória ActiveRow
-ja van, ami lehetővé teszi számunkra, hogy közvetlenül kiírjuk a nevét a
{$category->name}
segítségével. Ez egy gyakorlati példa arra, hogyan teheti áttekinthetőbbé a
csoportosítás a sablonokat és könnyítheti meg az adatokkal való munkát.
A |batch
szűrő
A szűrő lehetővé teszi az elemek listájának felosztását előre meghatározott számú elemet tartalmazó csoportokra. Ez a szűrő ideális olyan helyzetekben, amikor az adatokat több kisebb csoportban szeretné megjeleníteni, például a jobb áttekinthetőség vagy a vizuális elrendezés érdekében az oldalon.
Képzeljük el, hogy van egy listánk elemekkel, és szeretnénk őket listákban megjeleníteni, ahol mindegyik legfeljebb
három elemet tartalmaz. A |batch
szűrő használata ilyen esetben nagyon praktikus:
<ul>
{foreach ($items|batch: 3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
Ebben a példában az $items
lista kisebb csoportokra van osztva, ahol minden csoport ($batch
)
legfeljebb három elemet tartalmaz. Minden csoport ezután egy külön <ul>
listában jelenik meg.
Ha az utolsó csoport nem tartalmaz elegendő elemet a kívánt szám eléréséhez, a szűrő második paramétere lehetővé teszi annak meghatározását, hogy mivel legyen ez a csoport kiegészítve. Ez ideális az elemek esztétikus igazításához ott, ahol egy hiányos sor rendezetlennek tűnhet.
{foreach ($items|batch: 3, '—') as $batch}
...
{/foreach}
A {iterateWhile}
tag
Ugyanazokat a feladatokat, amelyeket a |group
szűrővel oldottunk meg, megmutatjuk a {iterateWhile}
tag használatával. A két megközelítés közötti fő különbség az, hogy a group
először feldolgozza és
csoportosítja az összes bemeneti adatot, míg a {iterateWhile}
a ciklusok menetét vezérli feltételekkel, így az
iteráció fokozatosan történik.
Először a kategóriákkal rendelkező táblázatot rajzoljuk ki az iterateWhile segítségével:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
</ul>
{/foreach}
Míg a {foreach}
a ciklus külső részét jelöli, azaz a listák kirajzolását minden kategóriához, addig a
{iterateWhile}
tag a belső részt jelöli, azaz az egyes elemeket. A záró tagben lévő feltétel azt mondja,
hogy az ismétlés addig tart, amíg az aktuális és a következő elem ugyanabba a kategóriába tartozik
($iterator->nextValue
a következő elem).
Ha a feltétel mindig teljesülne, akkor a belső ciklusban az összes elem kirajzolódna:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
Az eredmény így fog kinézni:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>PHP</li>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Mire jó az iterateWhile ilyen használata? Ha a táblázat üres és nem tartalmaz elemeket, nem íródik ki üres
<ul></ul>
.
Ha a feltételt a nyitó {iterateWhile}
tagben adjuk meg, akkor a viselkedés megváltozik: a feltétel (és a
következő elemre való áttérés) már a belső ciklus elején végrehajtódik, nem a végén. Tehát míg a feltétel
nélküli {iterateWhile}
-be mindig belépünk, addig a {iterateWhile $cond}
-be csak a $cond
feltétel teljesülése esetén. És ezzel egyidejűleg a következő elem beíródik az $item
-be.
Ez hasznos például abban a helyzetben, amikor minden kategória első elemét más módon szeretnénk kirajzolni, például így:
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
Az eredeti kódot úgy módosítjuk, hogy először kirajzoljuk az első elemet, majd a belső {iterateWhile}
ciklusban kirajzoljuk a többi elemet ugyanabból a kategóriából:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Egy cikluson belül több belső ciklust is létrehozhatunk, és akár beágyazhatjuk őket. Így lehetne például alkategóriákat csoportosítani stb.
Tegyük fel, hogy a táblázatban van még egy subcategoryId
oszlop, és amellett, hogy minden kategória külön
<ul>
-ben van, minden alkategória külön <ol>
-ban:
{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}