Skip to content

Commit

Permalink
iteratewhile -> grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed May 14, 2024
1 parent 923a3a3 commit f33f439
Show file tree
Hide file tree
Showing 34 changed files with 3,655 additions and 3,638 deletions.
214 changes: 214 additions & 0 deletions latte/bg/cookbook/grouping.texy
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
Всичко, което винаги сте искали да знаете за {iterateWhile}
***********************************************************

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

Да предположим, че имаме следната таблица в базата данни, в която елементите са разделени на категории:

| id | categoryId | 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->categoryId === $iterator->nextValue->categoryId}
</ul>
{/foreach}
```

Докато `{foreach}` се отнася до външната част на цикъла, т.е. изготвянето на списъци за всяка категория, таговете `{iterateWhile}` се отнасят до вътрешната част, т.е. отделните елементи.
Условието в крайния таг гласи, че повторението ще продължи, докато текущият и следващият елемент принадлежат към една и съща категория (`$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 $prevCategoryId = null}
{foreach $items as $item}
{if $item->categoryId !== $prevCategoryId}
{* the category has changed *}

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

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

{do $prevCategoryId = $item->categoryId}
{/if}

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

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

Този код обаче е неразбираем и неинтуитивен. Връзката между началния и крайния HTML таг не е съвсем ясна. От пръв поглед не е ясно дали има грешка. А това изисква помощни променливи като `$prevCategoryId`.

За разлика от това решението с `{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->categoryId === $iterator->nextValue->categoryId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
```


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

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

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


Филтриране | партида .[#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}}
Loading

0 comments on commit f33f439

Please sign in to comment.