Vse, kar ste vedno želeli vedeti o združevanju v skupine

Pri delu s podatki v predlogah pogosto naletite na potrebo po njihovem združevanju v skupine ali posebnem prikazu glede na določena merila. V ta namen Latte ponuja več zmogljivih orodij.

Filter in funkcija |group omogočata učinkovito grupiranje podatkov na podlagi določenih meril, filter |batch olajša razdelitev podatkov v fiksne serije, oznaka {iterateWhile} pa ponuja možnost kompleksnejšega cikličnega nadzora s pogoji. Vsaka od teh oznak ponuja posebne možnosti za delo s podatki, zaradi česar sta nepogrešljivi orodji za dinamičen in strukturiran prikaz informacij v predlogah Latte.

Filter in funkcija group

Predstavljajte si tabelo podatkovne zbirke items s predmeti, razdeljenimi v kategorije:

id categoryId name
1 1 Apple
2 1 Banana
3 2 PHP
4 3 zelena
5 3 rdeča
6 3 Modra

Preprost seznam vseh elementov s predlogo Latte bi bil videti takole:

<ul>
{foreach $items as $item}
	<li>{$item->name}</li>
{/foreach}
</ul>

Če pa bi želeli, da so predmeti razvrščeni v skupine po kategorijah, jih moramo razdeliti tako, da ima vsaka kategorija svoj seznam. Rezultat bi bil potem videti takole:

<ul>
	<li>Apple</li>
	<li>Banana</li>
</ul>

<ul>
	<li>PHP</li>
</ul>

<ul>
	<li>Green</li>
	<li>Red</li>
	<li>Blue</li>
</ul>

Nalogo lahko enostavno in elegantno rešimo z uporabo spletne strani |group. Kot parameter navedemo categoryId, kar pomeni, da bodo elementi razdeljeni v manjša polja na podlagi vrednosti $item->categoryId (če bi bil $item polje, bi uporabili $item['categoryId']):

{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
	<ul>
		{foreach $categoryItems as $item}
			<li>{$item->name}</li>
		{/foreach}
	</ul>
{/foreach}

Filter lahko uporabimo tudi kot funkcijo v Latte, kar nam omogoča alternativno sintakso: {foreach group($items, categoryId) ...}.

Če želite elemente razvrstiti v skupine glede na bolj zapletena merila, lahko v parametru filter uporabite funkcijo. Če bi na primer elemente grupirali glede na dolžino njihovega imena, bi bilo to videti takole:

{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
	...
{/foreach}

Pomembno je opozoriti, da $categoryItems ni navadno polje, temveč objekt, ki se obnaša kot iterator. Za dostop do prvega elementa v skupini lahko uporabite first() funkcijo.

Zaradi te prilagodljivosti pri grupiranju podatkov je group izjemno uporabno orodje za predstavitev podatkov v predlogah Latte.

Vgnezdene zanke

Recimo, da imamo tabelo v zbirki podatkov z drugim stolpcem subcategoryId, ki opredeljuje podkategorije za vsak element. Vsako glavno kategorijo želimo prikazati v ločenem <ul> seznamu, vsako podkategorijo pa v ločeni ugnezdeni zanki. <ol> seznamu:

{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}

Povezava s podatkovno bazo Nette

Pokažimo, kako učinkovito uporabiti združevanje podatkov v kombinaciji s podatkovno bazo Nette. Predpostavimo, da delamo s tabelo items iz začetnega primera, ki je prek stolpca categoryId povezana s to tabelo categories:

categoryId name
1 Sadje
2 Jeziki
3 Barve

Podatke iz preglednice items naložimo z ukazom raziskovalca podatkov Nette Database Explorer $items = $db->table('items'). Med iteracijo nad temi podatki imamo možnost ne le dostopa do atributov, kot sta $item->name in $item->categoryId, temveč zaradi povezave s tabelo categories tudi do povezane vrstice v njej prek $item->category. Ta povezava lahko pokaže zanimive načine uporabe:

{foreach ($items|group: category) as $category => $categoryItems}
	<h1>{$category->name}</h1>
	<ul>
		{foreach $categoryItems as $item}
			<li>{$item->name}</li>
		{/foreach}
	</ul>
{/foreach}

V tem primeru uporabimo filter |group za združevanje po povezani vrstici $item->category, ne le po stolpcu categoryId. Tako dobimo ActiveRow dane kategorije v spremenljivki ključ, kar nam omogoča neposreden prikaz njenega imena z uporabo {$category->name}. To je praktični primer, kako lahko združevanje v skupine poenostavi predloge in olajša obdelavo podatkov.

Filter |batch

Filter omogoča razdelitev seznama elementov v skupine z vnaprej določenim številom elementov. Ta filter je idealen v primerih, ko želite podatke predstaviti v več manjših skupinah, na primer zaradi boljše preglednosti ali vizualne organizacije na strani.

Predstavljajte si, da imamo seznam elementov in jih želimo prikazati v seznamih, od katerih vsak vsebuje največ tri elemente. Uporaba filtra |batch je v takem primeru zelo praktična:

<ul>
{foreach ($items|batch: 3) as $batch}
	{foreach $batch as $item}
		<li>{$item->name}</li>
	{/foreach}
{/foreach}
</ul>

V tem primeru je seznam $items razdeljen na manjše skupine, pri čemer vsaka skupina ($batch) vsebuje največ tri elemente. Vsaka skupina se nato prikaže v ločenem <ul> seznamu.

Če zadnja skupina ne vsebuje dovolj elementov, da bi dosegli želeno število, lahko z drugim parametrom filtra določite, s čim bo ta skupina dopolnjena. To je idealno za estetsko poravnavo elementov, kjer bi nepopolna vrstica lahko bila videti neurejena.

{foreach ($items|batch: 3, '—') as $batch}
	...
{/foreach}

Oznaka {iterateWhile}

Ista opravila, ki smo jih obravnavali s filtrom |group, bomo prikazali z uporabo oznake {iterateWhile}. Glavna razlika med pristopoma je, da group najprej obdela in združi vse vhodne podatke, medtem ko {iterateWhile} nadzoruje potek ciklov s pogoji, zato iteracija poteka zaporedno.

Najprej narišemo tabelo s kategorijami z uporabo iterateWhile:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}</li>
		{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
	</ul>
{/foreach}

Medtem ko {foreach} označuje zunanji del cikla, tj. risanje seznamov za vsako kategorijo, oznaka {iterateWhile} označuje notranji del, tj. posamezne elemente. Pogoj v končni oznaki pove, da se bo ponavljanje nadaljevalo, dokler trenutni in naslednji element pripadata isti kategoriji ($iterator->nextValue je naslednji element).

Če bi bil pogoj vedno izpolnjen, bi bili v notranjem ciklu narisani vsi elementi:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}
		{/iterateWhile true}
	</ul>
{/foreach}

Rezultat bo videti takole:

<ul>
	<li>Apple</li>
	<li>Banana</li>
	<li>PHP</li>
	<li>Green</li>
	<li>Red</li>
	<li>Blue</li>
</ul>

Kako se na ta način uporablja iterateWhile? Ko je tabela prazna in ne vsebuje elementov, ni praznih <ul></ul> se izpiše.

Če pogoj določimo v začetni oznaki {iterateWhile}, se obnašanje spremeni: pogoj (in prehod na naslednji element) se izvede na začetku notranjega cikla in ne na koncu. Tako, medtem ko vedno vnesemo {iterateWhile} brez pogojev, vnesemo {iterateWhile $cond} šele, ko je izpolnjen pogoj $cond. Hkrati se naslednji element zapiše v $item.

To je uporabno na primer v primeru, ko želimo prvi element v vsaki kategoriji prikazati drugače, na primer takole:

<h1>Apple</h1>
<ul>
	<li>Banana</li>
</ul>

<h1>PHP</h1>
<ul>
</ul>

<h1>Green</h1>
<ul>
	<li>Red</li>
	<li>Blue</li>
</ul>

Prvotno kodo spremenimo tako, da najprej prikažemo prvi element, nato pa v notranjem ciklu {iterateWhile} prikažemo druge elemente iz iste kategorije:

{foreach $items as $item}
	<h1>{$item->name}</h1>
	<ul>
		{iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
			<li>{$item->name}</li>
		{/iterateWhile}
	</ul>
{/foreach}

Znotraj enega cikla lahko ustvarimo več notranjih zank in jih celo ugnezdimo. Na ta način lahko na primer združimo podkategorije.

Recimo, da ima tabela še en stolpec subcategoryId, in poleg tega, da je vsaka kategorija v ločenem <ul>, vsaka podkategorija v ločenem ciklu <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}
različica: 3.0