Nette Documentation Preview

syntax
Все, что вы всегда хотели знать о {iterateWhile}
************************************************

.[perex]
Тег `{iterateWhile}` подходит для различных трюков в циклах foreach.

Предположим, у нас есть следующая таблица базы данных, в которой элементы разделены на категории:

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

Конечно, вывести элементы в цикле foreach в виде списка очень просто:

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

Но что делать, если вы хотите вывести каждую категорию в отдельный список? Другими словами, как решить задачу группировки элементов из линейного списка в цикле foreach. Вывод должен выглядеть следующим образом:

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

Мы покажем вам, как легко и элегантно можно решить эту задачу с помощью iterateWhile:

```latte
{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}</li>
		{/iterateWhile $item->catId === $iterator->nextValue->catId}
	</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}` .[#toc-solution-without-iteratewhile]
------------------------------------------------------------------

Если бы мы решали ту же задачу с помощью совершенно базовых конструкций систем шаблонов, например, в Twig, Blade или чистом PHP, то решение выглядело бы примерно так:

```latte
{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* the category has changed *}

		{* we close the previous <ul>, if it is not the first item *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* we will open a new list *}
		<ul>

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

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

{if $prevCatId !== null}
	{* we close the last list *}
	</ul>
{/if}
```

Однако этот код непонятен и неинтуитивен. Связь между открывающим и закрывающим HTML-тегами совершенно не ясна. С первого взгляда не ясно, есть ли ошибка. И для этого требуются вспомогательные переменные, такие как `$prevCatId`.

В отличие от этого, решение с `{iterateWhile}` чистое, понятное, не требует вспомогательных переменных и является надежным.


Условие в закрывающем теге .[#toc-condition-in-the-closing-tag]
---------------------------------------------------------------

Если указать условие в открывающем теге `{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->catId === $iterator->nextValue->catId}
			<li>{$item->name}</li>
		{/iterateWhile}
	</ul>
{/foreach}
```


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

Мы можем создать несколько внутренних циклов в одном цикле и даже вложить их друг в друга. Таким образом, например, можно сгруппировать подкатегории.

Предположим, что в таблице `subCatId` есть еще один столбец, и в дополнение к тому, что каждая категория будет находиться в отдельном столбце, каждая подкатегория будет находиться в отдельном столбце. `<ul>`, каждая подкатегория будет находиться в отдельной колонке `<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}
```


Фильтр | пакетный .[#toc-filter-batch]
--------------------------------------

Группировка линейных элементов также обеспечивается фильтром `batch`, в партии с фиксированным количеством элементов:

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

Его можно заменить на iterateWhile следующим образом:

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

{{leftbar: /@left-menu}}

Все, что вы всегда хотели знать о {iterateWhile}

Тег {iterateWhile} подходит для различных трюков в циклах foreach.

Предположим, у нас есть следующая таблица базы данных, в которой элементы разделены на категории:

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

Конечно, вывести элементы в цикле foreach в виде списка очень просто:

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

Но что делать, если вы хотите вывести каждую категорию в отдельный список? Другими словами, как решить задачу группировки элементов из линейного списка в цикле foreach. Вывод должен выглядеть следующим образом:

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

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

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

Мы покажем вам, как легко и элегантно можно решить эту задачу с помощью iterateWhile:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}</li>
		{/iterateWhile $item->catId === $iterator->nextValue->catId}
	</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}

Если бы мы решали ту же задачу с помощью совершенно базовых конструкций систем шаблонов, например, в Twig, Blade или чистом PHP, то решение выглядело бы примерно так:

{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* the category has changed *}

		{* we close the previous <ul>, if it is not the first item *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* we will open a new list *}
		<ul>

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

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

{if $prevCatId !== null}
	{* we close the last list *}
	</ul>
{/if}

Однако этот код непонятен и неинтуитивен. Связь между открывающим и закрывающим HTML-тегами совершенно не ясна. С первого взгляда не ясно, есть ли ошибка. И для этого требуются вспомогательные переменные, такие как $prevCatId.

В отличие от этого, решение с {iterateWhile} чистое, понятное, не требует вспомогательных переменных и является надежным.

Условие в закрывающем теге

Если указать условие в открывающем теге {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->catId === $iterator->nextValue->catId}
			<li>{$item->name}</li>
		{/iterateWhile}
	</ul>
{/foreach}

Вложенные циклы

Мы можем создать несколько внутренних циклов в одном цикле и даже вложить их друг в друга. Таким образом, например, можно сгруппировать подкатегории.

Предположим, что в таблице subCatId есть еще один столбец, и в дополнение к тому, что каждая категория будет находиться в отдельном столбце, каждая подкатегория будет находиться в отдельном столбце. <ul>, каждая подкатегория будет находиться в отдельной колонке <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}

Фильтр | пакетный

Группировка линейных элементов также обеспечивается фильтром batch, в партии с фиксированным количеством элементов:

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

Его можно заменить на iterateWhile следующим образом:

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