Tot ceea ce ați vrut să știți întotdeauna despre grupare

Atunci când lucrați cu date în șabloane, vă confruntați adesea cu necesitatea de a le grupa sau de a le afișa în mod specific în funcție de anumite criterii. În acest scop, Latte oferă mai multe instrumente puternice.

Filtrul și funcția |group permit gruparea eficientă a datelor pe baza unor criterii specificate, în timp ce filtrul |batch facilitează împărțirea datelor în loturi fixe, iar eticheta {iterateWhile} oferă posibilitatea unui control mai complex al ciclului cu condiții. Fiecare dintre aceste etichete oferă opțiuni specifice de lucru cu datele, ceea ce le face instrumente indispensabile pentru afișarea dinamică și structurată a informațiilor în șabloanele Latte.

Filtru și funcție group

Imaginați-vă un tabel de bază de date items cu articole împărțite în categorii:

id categoryId categoryId name
1 1 1 Apple

2 | 1 | 1 | Banană 3 | 2 | 2 | PHP 4 | 3 | 3 | Verde

5 3 Roșu

6 | 3 | 3 | Albastru

O listă simplă a tuturor elementelor folosind un șablon Latte ar arăta astfel:

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

Cu toate acestea, dacă am dori ca articolele să fie organizate în grupuri pe categorii, trebuie să le împărțim astfel încât fiecare categorie să aibă propria listă. Rezultatul ar arăta astfel:

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

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

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

Sarcina poate fi rezolvată ușor și elegant folosind |group. Specificăm categoryId ca parametru, ceea ce înseamnă că elementele vor fi împărțite în array-uri mai mici pe baza valorii $item->categoryId (dacă $item ar fi un array, am folosi $item['categoryId']):

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

Filtrul poate fi utilizat și ca o funcție în Latte, oferindu-ne o sintaxă alternativă: {foreach group($items, categoryId) ...}.

Dacă doriți să grupați elemente în funcție de criterii mai complexe, puteți utiliza o funcție în parametrul de filtrare. De exemplu, gruparea elementelor în funcție de lungimea numelui lor ar arăta în felul următor:

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

Este important să rețineți că $categoryItems nu este o matrice obișnuită, ci un obiect care se comportă ca un iterator. Pentru a accesa primul element din grup, puteți utiliza funcția first() funcție.

Această flexibilitate în gruparea datelor face din group un instrument extrem de util pentru prezentarea datelor în șabloanele Latte.

Bucle imbricate

Să presupunem că avem un tabel de bază de date cu o altă coloană subcategoryId care definește subcategoriile pentru fiecare articol. Dorim să afișăm fiecare categorie principală într-o coloană separată <ul> și fiecare subcategorie într-o listă separată, iar fiecare subcategorie într-o <ol> listă separată:

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

Conexiune cu baza de date Nette

Să arătăm cum să folosim în mod eficient gruparea datelor în combinație cu Nette Database. Să presupunem că lucrăm cu tabelul items din exemplul inițial, care este conectat prin coloana categoryId la acest tabel categories:

categoryId | categoryId | name |

|------------|------------| 1 | Fructe | Fructe |

2 Limbi
3 Culori

Încărcăm datele din tabelul items folosind comanda Nette Database Explorer $items = $db->table('items'). În timpul iterației asupra acestor date, avem posibilitatea nu numai de a accesa atribute precum $item->name și $item->categoryId, ci, datorită conexiunii cu tabelul categories, și rândul aferent din acesta prin intermediul $item->category. Această conexiune poate demonstra utilizări interesante:

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

În acest caz, folosim filtrul |group pentru a grupa după rândul conectat $item->category, nu doar după coloana categoryId. Acest lucru ne oferă ActiveRow din categoria dată în cheia variabilă, ceea ce ne permite să afișăm direct numele acesteia folosind {$category->name}. Acesta este un exemplu practic al modului în care gruparea poate simplifica șabloanele și facilita manipularea datelor.

Filtru |batch

Filtrul vă permite să împărțiți o listă de elemente în grupuri cu un număr prestabilit de elemente. Acest filtru este ideal pentru situațiile în care doriți să prezentați datele în mai multe grupuri mai mici, de exemplu, pentru o mai bună claritate sau organizare vizuală în pagină.

Imaginați-vă că avem o listă de elemente și dorim să le afișăm în liste, fiecare conținând maximum trei elemente. Utilizarea filtrului |batch este foarte practică într-un astfel de caz:

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

În acest exemplu, lista $items este împărțită în grupuri mai mici, fiecare grup ($batch) conținând cel mult trei elemente. Fiecare grup este apoi afișat într-o fereastră separată <ul> listă separată.

În cazul în care ultimul grup nu conține suficiente elemente pentru a ajunge la numărul dorit, al doilea parametru al filtrului vă permite să definiți cu ce va fi completat acest grup. Acest lucru este ideal pentru alinierea estetică a elementelor în cazul în care un rând incomplet ar putea părea dezordonat.

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

Etichetă {iterateWhile}

Vom demonstra aceleași sarcini pe care le-am abordat cu filtrul |group folosind eticheta {iterateWhile}. Principala diferență între cele două abordări este că group procesează și grupează mai întâi toate datele de intrare, în timp ce {iterateWhile} controlează progresul ciclurilor cu condiții, astfel încât iterația are loc secvențial.

Mai întâi, desenăm un tabel cu categorii folosind iterateWhile:

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

În timp ce {foreach} marchează partea exterioară a ciclului, adică desenarea listelor pentru fiecare categorie, eticheta {iterateWhile} marchează partea interioară, adică elementele individuale. Condiția din tag-ul end spune că repetiția va continua atâta timp cât elementul curent și cel următor aparțin aceleiași categorii ($iterator->nextValue este următorul element).

Dacă această condiție ar fi întotdeauna îndeplinită, toate elementele ar fi desenate în ciclul interior:

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

Rezultatul va arăta astfel:

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

Care este utilizarea iterateWhile în acest mod? Atunci când tabelul este gol și nu conține niciun element, nu este necesară o operațiune de tip "empty <ul></ul> este tipărit.

Dacă specificăm condiția în deschiderea etichetei {iterateWhile}, comportamentul se schimbă: condiția (și tranziția la următorul element) se realizează la începutul ciclului interior, nu la sfârșitul acestuia. Astfel, în timp ce întotdeauna se introduce {iterateWhile} fără condiții, se introduce {iterateWhile $cond} numai atunci când este îndeplinită condiția $cond. Și, în același timp, următorul element este scris în $item.

Acest lucru este util, de exemplu, în situația în care dorim să redăm primul element din fiecare categorie în mod diferit, astfel:

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

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

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

Modificăm codul original astfel încât să redăm mai întâi primul element și apoi, în ciclul interior {iterateWhile}, să redăm celelalte elemente din aceeași categorie:

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

În cadrul unui ciclu, putem crea mai multe bucle interioare și chiar le putem anina. În acest fel, subcategoriile ar putea fi grupate, de exemplu.

Să presupunem că tabelul are o altă coloană subcategoryId, și pe lângă faptul că fiecare categorie se află într-o coloană separată <ul>, fiecare subcategorie într-o coloană separată <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}
versiune: 3.0