Nette Documentation Preview

syntax
Всичко, което винаги сте искали да знаете за групирането
********************************************************

.[perex]
Когато работите с данни в шаблони, често се сблъсквате с необходимостта да ги групирате или да ги показвате конкретно по определени критерии. За тази цел Latte предлага няколко мощни инструмента.

Филтърът и функцията `|group` позволяват ефективно групиране на данните въз основа на определени критерии, докато филтърът `|batch` улеснява разделянето на данните на фиксирани партиди, а етикетът `{iterateWhile}` предоставя възможност за по-сложен контрол на циклите с условия.
Всеки от тези тагове предлага специфични възможности за работа с данни, което ги прави незаменими инструменти за динамично и структурирано показване на информация в шаблоните Latte.


Филтър и функция `group` .[#toc-filter-and-function-group]
==========================================================

Представете си таблица от базата данни `items` с елементи, разделени на категории:

| id | categoryId | name
|------------------
| 1 | 1 | Apple
| 2 | 1 | Банан
| 3 | 2 | PHP
| 4 | 3 | Зелен
| 5 | 3 | Червено
| 6 | 3 | Синьо

Един прост списък на всички елементи, използващ шаблон Latte, би изглеждал така:

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

Ако обаче искаме елементите да бъдат организирани в групи по категории, трябва да ги разделим така, че всяка категория да има собствен списък. Тогава резултатът би изглеждал така:

```latte
<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']`):

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

Филтърът може да се използва и като функция в Latte, което ни дава алтернативен синтаксис: `{foreach group($items, categoryId) ...}`.

Ако искате да групирате елементи по по-сложни критерии, можете да използвате функция в параметъра на филтъра. Например, групирането на елементи по дължината на името им ще изглежда по следния начин:

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

Важно е да се отбележи, че `$categoryItems` не е обикновен масив, а обект, който се държи като итератор. За да получите достъп до първия елемент в групата, можете да използвате [`first()` |latte:functions#first] функция.

Тази гъвкавост при групирането на данни прави `group` изключително полезен инструмент за представяне на данни в шаблони Latte.


Вложени цикли .[#toc-nested-loops]
----------------------------------

Да кажем, че имаме таблица в базата данни с още една колона `subcategoryId`, която определя подкатегории за всеки елемент. Искаме да покажем всяка основна категория в отделна `<ul>` списък и всяка подкатегория в отделен вложен цикъл `<ol>` списък:

```latte
{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 .[#toc-connection-with-nette-database]
------------------------------------------------------------------

Нека да покажем как ефективно да използваме групирането на данни в комбинация с Nette Database. Да предположим, че работим с таблицата `items` от първоначалния пример, която е свързана чрез колоната `categoryId` с тази таблица `categories`:

| categoryId | name |
|------------|------------|
| 1 | Fruits |
| 2 | Езици |
| 3 | Цветове |

Зареждаме данни от таблицата `items`, като използваме командата на Nette Database Explorer `$items = $db->table('items')`. По време на итерацията над тези данни имаме възможност не само за достъп до атрибути като `$item->name` и `$item->categoryId`, но благодарение на връзката с таблицата `categories` и до свързания ред в нея чрез `$item->category`. Тази връзка може да демонстрира интересни приложения:

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

В този случай използваме филтъра `|group`, за да групираме по свързания ред `$item->category`, а не само по колоната `categoryId`. Това ни дава `ActiveRow` на дадената категория в променливата ключ, което ни позволява директно да покажем нейното име с помощта на `{$category->name}`. Това е практически пример за това как групирането може да опрости шаблоните и да улесни обработката на данни.


Филтър `|batch` .[#toc-filter-batch]
====================================

Филтърът ви позволява да разделите списък от елементи на групи с предварително определен брой елементи. Този филтър е идеален за ситуации, в които искате да представите данните в няколко по-малки групи, например за по-добра яснота или визуална организация на страницата.

Представете си, че имаме списък с елементи и искаме да ги покажем в списъци, всеки от които съдържа максимум три елемента. Използването на филтъра `|batch` е много практично в такъв случай:

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

В този пример списъкът `$items` е разделен на по-малки групи, като всяка група (`$batch`) съдържа до три елемента. След това всяка група се показва в отделен `<ul>` списък.

Ако последната група не съдържа достатъчно елементи, за да се достигне желаният брой, вторият параметър на филтъра ви позволява да определите с какво ще бъде допълнена тази група. Това е идеално за естетическо подравняване на елементи, при които непълен ред може да изглежда безредно.

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


Етикет `{iterateWhile}` .[#toc-tag-iteratewhile]
================================================

Ще демонстрираме същите задачи, които разгледахме с филтъра `|group`, като използваме тага `{iterateWhile}`. Основната разлика между двата подхода е, че `group` първо обработва и групира всички входни данни, докато `{iterateWhile}` контролира хода на циклите с условия, така че итерацията се извършва последователно.

Първо, съставяме таблица с категории, като използваме iterateWhile:

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

Докато тагът `{foreach}` маркира външната част на цикъла, т.е. изготвянето на списъци за всяка категория, тагът `{iterateWhile}` маркира вътрешната част, т.е. отделните елементи.
Условието в тага end казва, че повторението ще продължи, докато текущият и следващият елемент принадлежат към една и съща категория (`$iterator->nextValue` е [следващият елемент |/tags#$iterator]).

Ако условието беше винаги изпълнено, всички елементи щяха да бъдат изведени във вътрешния цикъл:

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

Резултатът ще изглежда така:

```latte
<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`.

Това е полезно например в ситуация, в която искаме да визуализираме първия елемент във всяка категория по различен начин, например по следния начин:

```latte
<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}` да визуализираме останалите елементи от същата категория:

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

В рамките на един цикъл можем да създадем няколко вътрешни цикъла и дори да ги вложим. По този начин могат да се групират подкатегории, например.

Да предположим, че таблицата има още една колона `subcategoryId`, и освен че всяка категория е в отделна `<ul>`, всяка подкатегория в отделна `<ol>`:

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


{{leftbar: /@left-menu}}

Всичко, което винаги сте искали да знаете за групирането

Когато работите с данни в шаблони, често се сблъсквате с необходимостта да ги групирате или да ги показвате конкретно по определени критерии. За тази цел Latte предлага няколко мощни инструмента.

Филтърът и функцията |group позволяват ефективно групиране на данните въз основа на определени критерии, докато филтърът |batch улеснява разделянето на данните на фиксирани партиди, а етикетът {iterateWhile} предоставя възможност за по-сложен контрол на циклите с условия. Всеки от тези тагове предлага специфични възможности за работа с данни, което ги прави незаменими инструменти за динамично и структурирано показване на информация в шаблоните Latte.

Филтър и функция group

Представете си таблица от базата данни items с елементи, разделени на категории:

id categoryId name
1 1 Apple
2 1 Банан
3 2 PHP
4 3 Зелен
5 3 Червено
6 3 Синьо

Един прост списък на всички елементи, използващ шаблон 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

Нека да покажем как ефективно да използваме групирането на данни в комбинация с Nette Database. Да предположим, че работим с таблицата items от първоначалния пример, която е свързана чрез колоната categoryId с тази таблица categories:

categoryId name
1 Fruits
2 Езици
3 Цветове

Зареждаме данни от таблицата 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, за да групираме по свързания ред $item->category, а не само по колоната categoryId. Това ни дава ActiveRow на дадената категория в променливата ключ, което ни позволява директно да покажем нейното име с помощта на {$category->name}. Това е практически пример за това как групирането може да опрости шаблоните и да улесни обработката на данни.

Филтър |batch

Филтърът ви позволява да разделите списък от елементи на групи с предварително определен брой елементи. Този филтър е идеален за ситуации, в които искате да представите данните в няколко по-малки групи, например за по-добра яснота или визуална организация на страницата.

Представете си, че имаме списък с елементи и искаме да ги покажем в списъци, всеки от които съдържа максимум три елемента. Използването на филтъра |batch е много практично в такъв случай:

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

В този пример списъкът $items е разделен на по-малки групи, като всяка група ($batch) съдържа до три елемента. След това всяка група се показва в отделен <ul> списък.

Ако последната група не съдържа достатъчно елементи, за да се достигне желаният брой, вторият параметър на филтъра ви позволява да определите с какво ще бъде допълнена тази група. Това е идеално за естетическо подравняване на елементи, при които непълен ред може да изглежда безредно.

{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} маркира вътрешната част, т.е. отделните елементи. Условието в тага end казва, че повторението ще продължи, докато текущият и следващият елемент принадлежат към една и съща категория ($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}

В рамките на един цикъл можем да създадем няколко вътрешни цикъла и дори да ги вложим. По този начин могат да се групират подкатегории, например.

Да предположим, че таблицата има още една колона 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}