Minden, amit mindig is tudni akartál a csoportosításról
Amikor sablonokban dolgozik az adatokkal, gyakran találkozik azzal az igénnyel, hogy csoportosítani vagy bizonyos kritériumok szerint megjeleníteni kell őket. Erre a célra a Latte számos hatékony eszközt kínál.
A |group
szűrő és funkció lehetővé teszi az adatok hatékony csoportosítását meghatározott kritériumok
alapján, míg a |batch
szűrő megkönnyíti az adatok rögzített tételekre való felosztását, a
{iterateWhile}
címke pedig lehetőséget biztosít a feltételek segítségével történő összetettebb
ciklusszabályozásra. E címkék mindegyike speciális lehetőségeket kínál az adatokkal való munkához, így
nélkülözhetetlen eszközök az információk dinamikus és strukturált megjelenítéséhez a Latte sablonokban.
Szűrő és funkció group
Képzeljünk el egy items
adatbázis-táblát, amelyben kategóriákba sorolt tételek találhatók:
id | categoryId | name | |
---|---|---|---|
1 | 1 | 1 | Apple |
2 | 1 | Banán | |
3 | 2 | PHP | |
4 | 3 | Zöld | |
5 | 3 | Piros | |
6 | 3 | Kék |
Az összes elem egyszerű listája a Latte sablon használatával így nézne ki:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Ha azonban azt szeretnénk, hogy az elemeket kategóriák szerint csoportosítani tudjuk, akkor úgy kell felosztanunk őket, hogy minden kategóriának saját listája legyen. Az eredmény így nézne ki:
<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 megadjuk a
categoryId
címet, ami azt jelenti, hogy az elemeket a $item->categoryId
érték alapján kisebb
tömbökre osztjuk fel (ha a $item
egy tömb lenne, akkor a következővel dolgoznánk
$item['categoryId']
):
{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
A szűrő a Latte-ban függvényként is használható, így alternatív szintaxist kapunk:
{foreach group($items, categoryId) ...}
.
Ha összetettebb kritériumok szerint szeretnénk csoportosítani az elemeket, akkor a szűrő paraméterében használhatunk függvényt. Például az elemek csoportosítása a nevük hossza alapján így nézne ki:
{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
...
{/foreach}
Fontos megjegyezni, hogy a $categoryItems
nem egy hagyományos tömb, hanem egy olyan objektum, amely
iterátorként viselkedik. A csoport első elemének eléréséhez használhatja a first()
függvényt.
Az adatok csoportosításának ez a rugalmassága teszi a group
-t kivételesen hasznos eszközzé az adatok Latte
sablonokban történő bemutatásához.
Beágyazott hurkok
Tegyük fel, hogy van egy adatbázis-táblánk egy másik subcategoryId
oszloppal, amely az egyes tételek
alkategóriáit határozza meg. Minden egyes fő kategóriát egy különálló <ul>
listában, és minden
alkategóriát egy külön beágyazott listában. <ol>
listában:
{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 adatbázissal
Megmutatjuk, hogyan lehet hatékonyan használni az adatcsoportosítást a Nette Adatbázissal kombinálva. Tegyük fel, hogy
az eredeti példában szereplő items
táblával dolgozunk, amely a categoryId
oszlopon keresztül
kapcsolódik ehhez a categories
táblához:
categoryId | név |
---|---|
1 | Gyümölcsök |
2 | Nyelvek |
3 | Színek |
Az adatokat a items
táblából a Nette Database Explorer $items = $db->table('items')
parancsával töltjük be. Az ezeken az adatokon való iteráció során nemcsak az olyan attribútumokhoz férhetünk hozzá,
mint a $item->name
és a $item->categoryId
, hanem a categories
táblával való
kapcsolatnak köszönhetően a $item->category
segítségével a tábla kapcsolódó sorához is. Ez a kapcsolat
érdekes felhasználási lehetőségeket mutathat fel:
{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ővel a $item->category
kapcsolódó sora szerint csoportosítunk,
nem csak a categoryId
oszlop szerint. Ezáltal a változó kulcsában megkapjuk az adott kategória
ActiveRow
címét, így a {$category->name}
segítségével közvetlenül megjeleníthetjük a
nevét. Ez egy gyakorlati példa arra, hogy a csoportosítás hogyan egyszerűsítheti a sablonokat és könnyítheti meg az
adatkezelést.
Szűrő |batch
A szűrő lehetővé teszi, hogy az elemek listáját előre meghatározott számú elemet tartalmazó csoportokra ossza. 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 az oldalon való vizuális rendezés érdekében.
Képzeljük el, hogy van egy elemeket tartalmazó listánk, és azokat listákban szeretnénk megjeleníteni, amelyek
mindegyike legfeljebb három elemet tartalmazhat. 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 a $items
listát kisebb csoportokra osztjuk, és minden csoport ($batch
)
legfeljebb három elemet tartalmaz. Ezután minden csoport egy különálló <ul>
listában.
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 egészüljön ki ez a csoport. Ez ideális az elemek esztétikai összehangolására, ahol egy hiányos sor rendezetlennek tűnhet.
{foreach ($items|batch: 3, '—') as $batch}
...
{/foreach}
Címke {iterateWhile}
A {iterateWhile}
címkével ugyanazokat a feladatokat mutatjuk be, mint amiket a |group
szűrővel
kezeltünk. A fő különbség a két megközelítés között az, hogy a group
először feldolgozza és
csoportosítja az összes bemeneti adatot, míg a {iterateWhile}
a feltételekkel rendelkező ciklusok
előrehaladását vezérli, így az iteráció szekvenciálisan történik.
Először egy kategóriákat tartalmazó táblázatot rajzolunk 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 az egyes kategóriákhoz tartozó listák rajzolását,
addig a {iterateWhile}
címke a belső részt, azaz az egyes elemeket. Az end tagben lévő feltétel azt mondja,
hogy az ismétlés addig folytatódik, amíg az aktuális és a következő elem ugyanahhoz a kategóriához tartozik
($iterator->nextValue
a következő elem).
Ha a feltétel mindig teljesülne, akkor a belső ciklusban minden 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>
Mi a haszna az iterateWhile-nak ebben a formában? Ha a táblázat üres, és nem tartalmaz elemeket, nincs üres
<ul></ul>
kerül kiírásra.
Ha a feltételt a nyitó {iterateWhile}
címkében adjuk meg, a viselkedés megváltozik: a feltétel (és a
következő elemre való átmenet) a belső ciklus elején történik, nem pedig a végén. Így míg a
{iterateWhile}
mindig feltétel nélkül lép be, addig a {iterateWhile $cond}
csak akkor lép be, ha a
$cond
feltétel teljesül. És ezzel egyidejűleg a következő elemet írja be a $item
.
Ez hasznos például olyan helyzetben, amikor az egyes kategóriák első elemét másképp akarjuk megjeleníteni, 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 az első elemet rendereljük, majd a belső ciklusban
{iterateWhile}
rendereljük 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ő hurkot is létrehozhatunk, és akár egymásba is ágyazhatjuk őket. Így például az alkategóriákat is csoportosíthatjuk.
Tegyük fel, hogy a táblázatnak van egy másik oszlopa is: subcategoryId
, és amellett, hogy minden kategória
egy különálló <ul>
, minden alkategória egy külön <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}