Nette Documentation Preview

syntax
Wszystko, co zawsze chciałeś wiedzieć o grupowaniu
**************************************************

.[perex]
Podczas pracy z danymi w szablonach często pojawia się potrzeba ich grupowania lub wyświetlania według określonych kryteriów. W tym celu Latte oferuje kilka zaawansowanych narzędzi.

Filtr i funkcja `|group` pozwalają na efektywne grupowanie danych w oparciu o określone kryteria, podczas gdy filtr `|batch` ułatwia dzielenie danych na stałe partie, a tag `{iterateWhile}` zapewnia możliwość bardziej złożonej kontroli cyklu z warunkami.
Każdy z tych tagów oferuje określone opcje pracy z danymi, co czyni je niezbędnymi narzędziami do dynamicznego i uporządkowanego wyświetlania informacji w szablonach Latte.


Filtr i funkcja `group` .[#toc-filter-and-function-group]
=========================================================

Wyobraźmy sobie tabelę bazy danych `items` z elementami podzielonymi na kategorie:

| id | categoryId | name
|------------------
| 1 | 1 | Jabłko
| 2 | 1 | Banana
| 3 | 2 | PHP
| 4 | 3 | Zielony
| 5 | 3 | Czerwony
| 6 | 3 | Niebieski

Prosta lista wszystkich elementów przy użyciu szablonu Latte wyglądałaby następująco:

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

Jeśli jednak chcemy, aby przedmioty były zorganizowane w grupy według kategorii, musimy podzielić je tak, aby każda kategoria miała własną listę. Wynik wyglądałby wtedy następująco:

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

Zadanie to można łatwo i elegancko rozwiązać za pomocą `|group`. Jako parametr podajemy `categoryId`, co oznacza, że elementy zostaną podzielone na mniejsze tablice w oparciu o wartość `$item->categoryId` (gdyby `$item` była tablicą, użylibyśmy `$item['categoryId']`):

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

Filtr może być również użyty jako funkcja w Latte, dając nam alternatywną składnię: `{foreach group($items, categoryId) ...}`.

Jeśli chcesz pogrupować elementy według bardziej złożonych kryteriów, możesz użyć funkcji w parametrze filtra. Na przykład grupowanie elementów według długości ich nazwy wyglądałoby następująco:

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

Należy zauważyć, że `$categoryItems` nie jest zwykłą tablicą, ale obiektem, który zachowuje się jak iterator. Aby uzyskać dostęp do pierwszego elementu w grupie, można użyć funkcji [`first()` |latte:functions#first] function.

Ta elastyczność w grupowaniu danych sprawia, że `group` jest wyjątkowo przydatnym narzędziem do prezentacji danych w szablonach Latte.


Zagnieżdżone pętle .[#toc-nested-loops]
---------------------------------------

Załóżmy, że mamy tabelę bazy danych z inną kolumną `subcategoryId`, która definiuje podkategorie dla każdego elementu. Chcemy wyświetlić każdą główną kategorię na osobnej liście `<ul>` a każdą podkategorię na osobnej zagnieżdżonej liście `<ol>` liście:

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


Połączenie z bazą danych Nette .[#toc-connection-with-nette-database]
---------------------------------------------------------------------

Pokażmy, jak efektywnie wykorzystać grupowanie danych w połączeniu z Nette Database. Załóżmy, że pracujemy z tabelą `items` z początkowego przykładu, która jest połączona przez kolumnę `categoryId` z tabelą `categories`:

| categoryId | name |
|------------|------------|
| 1 | Fruits |
| 2 | Języki |
| 3 | Kolory |

Wczytujemy dane z tabeli `items` za pomocą polecenia Nette Database Explorer `$items = $db->table('items')`. Podczas iteracji po tych danych mamy możliwość nie tylko dostępu do atrybutów takich jak `$item->name` i `$item->categoryId`, ale dzięki połączeniu z tabelą `categories`, także do powiązanego z nią wiersza poprzez `$item->category`. To połączenie może wykazać interesujące zastosowania:

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

W tym przypadku używamy filtra `|group` do grupowania według połączonego wiersza `$item->category`, a nie tylko według kolumny `categoryId`. Daje nam to `ActiveRow` danej kategorii w kluczu zmiennej, co pozwala nam bezpośrednio wyświetlić jej nazwę za pomocą `{$category->name}`. Jest to praktyczny przykład tego, jak grupowanie może uprościć szablony i ułatwić obsługę danych.


Filtr `|batch` .[#toc-filter-batch]
===================================

Filtr umożliwia podzielenie listy elementów na grupy o z góry określonej liczbie elementów. Ten filtr jest idealny w sytuacjach, gdy chcesz przedstawić dane w kilku mniejszych grupach, na przykład dla lepszej przejrzystości lub wizualnej organizacji na stronie.

Wyobraźmy sobie, że mamy listę elementów i chcemy wyświetlić je na listach, z których każda zawiera maksymalnie trzy elementy. Użycie filtra `|batch` jest w takim przypadku bardzo praktyczne:

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

W tym przykładzie lista `$items` jest podzielona na mniejsze grupy, z których każda (`$batch`) zawiera maksymalnie trzy elementy. Każda grupa jest następnie wyświetlana na osobnej `<ul>` liście.

Jeśli ostatnia grupa nie zawiera wystarczającej liczby elementów, aby osiągnąć pożądaną liczbę, drugi parametr filtra pozwala określić, czym ta grupa zostanie uzupełniona. Jest to idealne rozwiązanie do estetycznego wyrównania elementów, w przypadku których niekompletny wiersz może wyglądać na nieuporządkowany.

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


Tag `{iterateWhile}` .[#toc-tag-iteratewhile]
=============================================

Zademonstrujemy te same zadania, którymi zajmowaliśmy się z filtrem `|group` przy użyciu tagu `{iterateWhile}`. Główna różnica między tymi dwoma podejściami polega na tym, że `group` najpierw przetwarza i grupuje wszystkie dane wejściowe, podczas gdy `{iterateWhile}` kontroluje postęp cykli z warunkami, więc iteracja odbywa się sekwencyjnie.

Najpierw rysujemy tabelę z kategoriami za pomocą iterateWhile:

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

Podczas gdy `{foreach}` oznacza zewnętrzną część cyklu, tj. rysowanie list dla każdej kategorii, znacznik `{iterateWhile}` oznacza część wewnętrzną, tj. poszczególne elementy.
Warunek w znaczniku end mówi, że powtarzanie będzie kontynuowane tak długo, jak bieżący i następny element należą do tej samej kategorii (`$iterator->nextValue` jest [następnym elementem |/tags#$iterator]).

Gdyby warunek był zawsze spełniony, wszystkie elementy byłyby rysowane w wewnętrznym cyklu:

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

Wynik będzie wyglądał następująco:

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

Jaki jest pożytek z iterateWhile w ten sposób? Gdy tabela jest pusta i nie zawiera żadnych elementów, nie jest wypisywana pusta wartość `<ul></ul>` jest drukowany.

Jeśli określimy warunek w otwierającym tagu `{iterateWhile}`, zachowanie ulegnie zmianie: warunek (i przejście do następnego elementu) jest wykonywany na początku wewnętrznego cyklu, a nie na końcu.
Tak więc, podczas gdy zawsze wchodzisz na `{iterateWhile}` bez warunków, wchodzisz na `{iterateWhile $cond}` tylko wtedy, gdy warunek `$cond` jest spełniony. W tym samym czasie następny element jest zapisywany w `$item`.

Jest to przydatne na przykład w sytuacji, gdy chcemy wyrenderować pierwszy element w każdej kategorii w inny sposób, na przykład w następujący sposób:

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

Modyfikujemy oryginalny kod w taki sposób, że najpierw renderujemy pierwszy element, a następnie w wewnętrznym cyklu `{iterateWhile}` renderujemy pozostałe elementy z tej samej kategorii:

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

W ramach jednego cyklu możemy utworzyć wiele wewnętrznych pętli, a nawet je zagnieżdżać. W ten sposób można na przykład grupować podkategorie.

Załóżmy, że tabela ma jeszcze jedną kolumnę `subcategoryId`, a oprócz tego każda kategoria znajduje się w osobnej kolumnie. `<ul>`a każda podkategoria w osobnej `<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}}

Wszystko, co zawsze chciałeś wiedzieć o grupowaniu

Podczas pracy z danymi w szablonach często pojawia się potrzeba ich grupowania lub wyświetlania według określonych kryteriów. W tym celu Latte oferuje kilka zaawansowanych narzędzi.

Filtr i funkcja |group pozwalają na efektywne grupowanie danych w oparciu o określone kryteria, podczas gdy filtr |batch ułatwia dzielenie danych na stałe partie, a tag {iterateWhile} zapewnia możliwość bardziej złożonej kontroli cyklu z warunkami. Każdy z tych tagów oferuje określone opcje pracy z danymi, co czyni je niezbędnymi narzędziami do dynamicznego i uporządkowanego wyświetlania informacji w szablonach Latte.

Filtr i funkcja group

Wyobraźmy sobie tabelę bazy danych items z elementami podzielonymi na kategorie:

id categoryId name
1 1 Jabłko
2 1 Banana
3 2 PHP
4 3 Zielony
5 3 Czerwony
6 3 Niebieski

Prosta lista wszystkich elementów przy użyciu szablonu Latte wyglądałaby następująco:

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

Jeśli jednak chcemy, aby przedmioty były zorganizowane w grupy według kategorii, musimy podzielić je tak, aby każda kategoria miała własną listę. Wynik wyglądałby wtedy następująco:

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

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

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

Zadanie to można łatwo i elegancko rozwiązać za pomocą |group. Jako parametr podajemy categoryId, co oznacza, że elementy zostaną podzielone na mniejsze tablice w oparciu o wartość $item->categoryId (gdyby $item była tablicą, użylibyśmy $item['categoryId']):

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

Filtr może być również użyty jako funkcja w Latte, dając nam alternatywną składnię: {foreach group($items, categoryId) ...}.

Jeśli chcesz pogrupować elementy według bardziej złożonych kryteriów, możesz użyć funkcji w parametrze filtra. Na przykład grupowanie elementów według długości ich nazwy wyglądałoby następująco:

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

Należy zauważyć, że $categoryItems nie jest zwykłą tablicą, ale obiektem, który zachowuje się jak iterator. Aby uzyskać dostęp do pierwszego elementu w grupie, można użyć funkcji first() function.

Ta elastyczność w grupowaniu danych sprawia, że group jest wyjątkowo przydatnym narzędziem do prezentacji danych w szablonach Latte.

Zagnieżdżone pętle

Załóżmy, że mamy tabelę bazy danych z inną kolumną subcategoryId, która definiuje podkategorie dla każdego elementu. Chcemy wyświetlić każdą główną kategorię na osobnej liście <ul> a każdą podkategorię na osobnej zagnieżdżonej liście <ol> liście:

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

Połączenie z bazą danych Nette

Pokażmy, jak efektywnie wykorzystać grupowanie danych w połączeniu z Nette Database. Załóżmy, że pracujemy z tabelą items z początkowego przykładu, która jest połączona przez kolumnę categoryId z tabelą categories:

categoryId name
1 Fruits
2 Języki
3 Kolory

Wczytujemy dane z tabeli items za pomocą polecenia Nette Database Explorer $items = $db->table('items'). Podczas iteracji po tych danych mamy możliwość nie tylko dostępu do atrybutów takich jak $item->name i $item->categoryId, ale dzięki połączeniu z tabelą categories, także do powiązanego z nią wiersza poprzez $item->category. To połączenie może wykazać interesujące zastosowania:

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

W tym przypadku używamy filtra |group do grupowania według połączonego wiersza $item->category, a nie tylko według kolumny categoryId. Daje nam to ActiveRow danej kategorii w kluczu zmiennej, co pozwala nam bezpośrednio wyświetlić jej nazwę za pomocą {$category->name}. Jest to praktyczny przykład tego, jak grupowanie może uprościć szablony i ułatwić obsługę danych.

Filtr |batch

Filtr umożliwia podzielenie listy elementów na grupy o z góry określonej liczbie elementów. Ten filtr jest idealny w sytuacjach, gdy chcesz przedstawić dane w kilku mniejszych grupach, na przykład dla lepszej przejrzystości lub wizualnej organizacji na stronie.

Wyobraźmy sobie, że mamy listę elementów i chcemy wyświetlić je na listach, z których każda zawiera maksymalnie trzy elementy. Użycie filtra |batch jest w takim przypadku bardzo praktyczne:

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

W tym przykładzie lista $items jest podzielona na mniejsze grupy, z których każda ($batch) zawiera maksymalnie trzy elementy. Każda grupa jest następnie wyświetlana na osobnej <ul> liście.

Jeśli ostatnia grupa nie zawiera wystarczającej liczby elementów, aby osiągnąć pożądaną liczbę, drugi parametr filtra pozwala określić, czym ta grupa zostanie uzupełniona. Jest to idealne rozwiązanie do estetycznego wyrównania elementów, w przypadku których niekompletny wiersz może wyglądać na nieuporządkowany.

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

Tag {iterateWhile}

Zademonstrujemy te same zadania, którymi zajmowaliśmy się z filtrem |group przy użyciu tagu {iterateWhile}. Główna różnica między tymi dwoma podejściami polega na tym, że group najpierw przetwarza i grupuje wszystkie dane wejściowe, podczas gdy {iterateWhile} kontroluje postęp cykli z warunkami, więc iteracja odbywa się sekwencyjnie.

Najpierw rysujemy tabelę z kategoriami za pomocą iterateWhile:

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

Podczas gdy {foreach} oznacza zewnętrzną część cyklu, tj. rysowanie list dla każdej kategorii, znacznik {iterateWhile} oznacza część wewnętrzną, tj. poszczególne elementy. Warunek w znaczniku end mówi, że powtarzanie będzie kontynuowane tak długo, jak bieżący i następny element należą do tej samej kategorii ($iterator->nextValue jest następnym elementem).

Gdyby warunek był zawsze spełniony, wszystkie elementy byłyby rysowane w wewnętrznym cyklu:

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

Wynik będzie wyglądał następująco:

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

Jaki jest pożytek z iterateWhile w ten sposób? Gdy tabela jest pusta i nie zawiera żadnych elementów, nie jest wypisywana pusta wartość <ul></ul> jest drukowany.

Jeśli określimy warunek w otwierającym tagu {iterateWhile}, zachowanie ulegnie zmianie: warunek (i przejście do następnego elementu) jest wykonywany na początku wewnętrznego cyklu, a nie na końcu. Tak więc, podczas gdy zawsze wchodzisz na {iterateWhile} bez warunków, wchodzisz na {iterateWhile $cond} tylko wtedy, gdy warunek $cond jest spełniony. W tym samym czasie następny element jest zapisywany w $item.

Jest to przydatne na przykład w sytuacji, gdy chcemy wyrenderować pierwszy element w każdej kategorii w inny sposób, na przykład w następujący sposób:

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

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

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

Modyfikujemy oryginalny kod w taki sposób, że najpierw renderujemy pierwszy element, a następnie w wewnętrznym cyklu {iterateWhile} renderujemy pozostałe elementy z tej samej kategorii:

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

W ramach jednego cyklu możemy utworzyć wiele wewnętrznych pętli, a nawet je zagnieżdżać. W ten sposób można na przykład grupować podkategorie.

Załóżmy, że tabela ma jeszcze jedną kolumnę subcategoryId, a oprócz tego każda kategoria znajduje się w osobnej kolumnie. <ul>a każda podkategoria w osobnej <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}