Nette Documentation Preview

syntax
Wszystko, co kiedykolwiek chciałeś wiedzieć o {iterateWhile}.
*************************************************************

.[perex]
Znacznik `{iterateWhile}` jest przydatny do wszelkiego rodzaju sztuczek w pętlach foreach.

Załóżmy, że mamy następującą tabelę bazy danych, w której przedmioty są skategoryzowane:

| id  |  catId  |  name
|------------------
| 1   |      1  | Apple
| 2   |      1  | Banana
| 3   |      2  | PHP
| 4   |      3  | Green
| 5   |      3  | Red
| 6   |      3  | Blue

Renderowanie elementów w pętli foreach jako listy jest oczywiście proste:

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

Ale co jeśli chcielibyśmy, aby każda kategoria była na osobnej liście? Innymi słowy, rozwiązujemy problem, jak pogrupować elementy na liście liniowej w pętli foreach. Dane wyjściowe powinny wyglądać tak:

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

Zobaczymy jak łatwo i elegancko można rozwiązać to zadanie używając iterateWhile:

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

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

Gdyby warunek był zawsze spełniony, wszystkie elementy byłyby renderowane w wewnętrznej pętli:

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

Wynik wyglądałby tak:

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

Jakie jest zastosowanie iterateWhile? Czym różni się ono od rozwiązania, które pokazaliśmy na samym początku tego tutorialu? Różnica polega na tym, że jeśli tablica jest pusta i nie zawiera żadnych elementów, nie zostanie wypisana pusta tablica `<ul></ul>`.


Rozwiązanie bez `{iterateWhile}` .[#toc-solution-without-iteratewhile]
----------------------------------------------------------------------

Gdybyśmy mieli rozwiązać to samo zadanie używając bardzo podstawowych systemów templatowania, na przykład w Twigu, Blade, czy czystym PHP, rozwiązanie wyglądałoby coś takiego:

```latte
{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* kategoria zmieniona *}

		{* zamknij poprzedni, <ul> jeśli nie jest pierwszym elementem *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* otwórz nową listę *}
		<ul>

		{do $prevCatId = $item->catId}
	{/if}

	<li>{$item->name}</li>
{/foreach}

{if $prevCatId !== null}
	{* zamknij ostatnią listę *}
	</ul>
{/if}
```

Jednak ten kod jest niezrozumiały i nieintuicyjny. Związek pomiędzy otwierającymi i zamykającymi znacznikami HTML nie jest wcale jasny. Nie widać na pierwszy rzut oka, czy jest jakiś błąd. I wymaga zmiennych pomocniczych, takich jak `$prevCatId`.

W przeciwieństwie do tego, rozwiązanie `{iterateWhile}` jest czyste, jasne, nie potrzebuje zmiennych pomocniczych i jest bloatproof.


Warunek w tagu otwierającym .[#toc-condition-in-the-closing-tag]
----------------------------------------------------------------

Jeśli określisz warunek w znaczniku otwierającym `{iterateWhile}`, to zachowanie się zmieni: warunek (i przejście do następnego elementu) jest wykonywany na początku pętli wewnętrznej, a nie na końcu.
O ile więc `{iterateWhile}` bez warunku jest wpisywany zawsze, to `{iterateWhile $cond}` jest wpisywany dopiero po spełnieniu warunku `$cond`. I w tym samym czasie na stronę `$item` wchodzi kolejny element.

Co jest przydatne np. jeśli chcemy wyrenderować pierwszy element w każdej kategorii w inny sposób, np:

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

Zmodyfikuj oryginalny kod, najpierw renderując pierwszy element, a następnie renderując pozostałe elementy z tej samej kategorii w wewnętrznej pętli strony `{iterateWhile}`:

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


Pętle zagnieżdżone .[#toc-nested-loops]
---------------------------------------

Możemy tworzyć wiele pętli wewnętrznych w ramach jednej pętli, a nawet je zagnieżdżać. W ten sposób można by grupować np. podkategorie itp.

Załóżmy, że w tabeli `subCatId` jest jeszcze jedna kolumna i oprócz tego, że każda kategoria jest w osobnej `<ul>`każda podkategoria w osobnym `<ol>`:

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


Filtr |wszystkie .[#toc-filter-batch]
-------------------------------------

Filtr `batch` obsługuje również grupowanie elementów liniowych w partie o stałej liczbie elementów:

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

Można go zastąpić iterateWhile w następujący sposób:

```latte
<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>
```

{{leftbar: /@left-menu}}

Wszystko, co kiedykolwiek chciałeś wiedzieć o {iterateWhile}.

Znacznik {iterateWhile} jest przydatny do wszelkiego rodzaju sztuczek w pętlach foreach.

Załóżmy, że mamy następującą tabelę bazy danych, w której przedmioty są skategoryzowane:

id catId name
1 1 Apple
2 1 Banana
3 2 PHP
4 3 Green
5 3 Red
6 3 Blue

Renderowanie elementów w pętli foreach jako listy jest oczywiście proste:

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

Ale co jeśli chcielibyśmy, aby każda kategoria była na osobnej liście? Innymi słowy, rozwiązujemy problem, jak pogrupować elementy na liście liniowej w pętli foreach. Dane wyjściowe powinny wyglądać tak:

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

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

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

Zobaczymy jak łatwo i elegancko można rozwiązać to zadanie używając iterateWhile:

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

Podczas gdy {foreach} oznacza zewnętrzną część pętli, czyli renderowanie list dla każdej kategorii, znacznik {iterateWhile} oznacza część wewnętrzną, czyli poszczególne elementy. Warunek w znaczniku end mówi, że iteracja będzie trwała tak długo, jak długo 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 renderowane w wewnętrznej pętli:

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

Wynik wyglądałby tak:

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

Jakie jest zastosowanie iterateWhile? Czym różni się ono od rozwiązania, które pokazaliśmy na samym początku tego tutorialu? Różnica polega na tym, że jeśli tablica jest pusta i nie zawiera żadnych elementów, nie zostanie wypisana pusta tablica <ul></ul>.

Rozwiązanie bez {iterateWhile}

Gdybyśmy mieli rozwiązać to samo zadanie używając bardzo podstawowych systemów templatowania, na przykład w Twigu, Blade, czy czystym PHP, rozwiązanie wyglądałoby coś takiego:

{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* kategoria zmieniona *}

		{* zamknij poprzedni, <ul> jeśli nie jest pierwszym elementem *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* otwórz nową listę *}
		<ul>

		{do $prevCatId = $item->catId}
	{/if}

	<li>{$item->name}</li>
{/foreach}

{if $prevCatId !== null}
	{* zamknij ostatnią listę *}
	</ul>
{/if}

Jednak ten kod jest niezrozumiały i nieintuicyjny. Związek pomiędzy otwierającymi i zamykającymi znacznikami HTML nie jest wcale jasny. Nie widać na pierwszy rzut oka, czy jest jakiś błąd. I wymaga zmiennych pomocniczych, takich jak $prevCatId.

W przeciwieństwie do tego, rozwiązanie {iterateWhile} jest czyste, jasne, nie potrzebuje zmiennych pomocniczych i jest bloatproof.

Warunek w tagu otwierającym

Jeśli określisz warunek w znaczniku otwierającym {iterateWhile}, to zachowanie się zmieni: warunek (i przejście do następnego elementu) jest wykonywany na początku pętli wewnętrznej, a nie na końcu. O ile więc {iterateWhile} bez warunku jest wpisywany zawsze, to {iterateWhile $cond} jest wpisywany dopiero po spełnieniu warunku $cond. I w tym samym czasie na stronę $item wchodzi kolejny element.

Co jest przydatne np. jeśli chcemy wyrenderować pierwszy element w każdej kategorii w inny sposób, np:

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

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

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

Zmodyfikuj oryginalny kod, najpierw renderując pierwszy element, a następnie renderując pozostałe elementy z tej samej kategorii w wewnętrznej pętli strony {iterateWhile}:

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

Pętle zagnieżdżone

Możemy tworzyć wiele pętli wewnętrznych w ramach jednej pętli, a nawet je zagnieżdżać. W ten sposób można by grupować np. podkategorie itp.

Załóżmy, że w tabeli subCatId jest jeszcze jedna kolumna i oprócz tego, że każda kategoria jest w osobnej <ul>każda podkategoria w osobnym <ol>:

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

Filtr |wszystkie

Filtr batch obsługuje również grupowanie elementów liniowych w partie o stałej liczbie elementów:

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

Można go zastąpić iterateWhile w następujący sposób:

<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>