Skip to content

Commit

Permalink
x
Browse files Browse the repository at this point in the history
x
  • Loading branch information
dg committed May 14, 2024
1 parent 9ea6f45 commit c8faa54
Showing 1 changed file with 105 additions and 63 deletions.
168 changes: 105 additions & 63 deletions latte/cs/cookbook/grouping.texy
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ Všechno, co jste kdy chtěli vědět o seskupování
***********************************************

.[perex]
Značka `{iterateWhile}` se hodí na nejrůznější kejkle ve foreach cyklech.
Při práci s daty ve šablonách můžete často narazit na potřebu jejich seskupování nebo specifického zobrazení podle určitých kritérií. Latte pro tento účel nabízí hned několik silných nástrojů.

Filtr a funkce `|group` umožňují efektivní seskupení dat podle zadaného kritéria, filtr `|batch` zase usnadňuje rozdělení dat do pevně daných dávek a značka `{iterateWhile}` poskytuje možnost složitějšího řízení průběhu cyklů s podmínkami.
Každá z těchto značek nabízí specifické možnosti pro práci s daty, čímž se stávají nepostradatelnými nástroji pro dynamické a strukturované zobrazení informací v Latte šablonách.


Filtr a funkce `group`
----------------------

Dejme tomu, že máme následující databázovou tabulku, kde jsou položky rozdělené do kategorií:

Expand Down Expand Up @@ -44,22 +51,20 @@ Ale co kdybychom chtěli, aby každá kategorie byla v samostatném seznamu? Jin
</ul>
```

Rovnou si ukážeme, jak snadno a elegantně se dá úkol vyřešit pomocí iterateWhile:
Rovnou si ukážeme, jak snadno a elegantně se dá úkol vyřešit pomocí `|group`:

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

Zatímco `{foreach}` označuje vnější část cyklu, tedy vykreslování seznamů pro každou kategorii, tak značka `{iterateWhile}` označuje vnitřní část, tedy jednotlivé položky.
Podmínka v koncové značce říká, že opakování bude probíhat do té doby, dokud aktuální i následující prvek patří do stejné kategorie (`$iterator->nextValue` je [následující položka|/tags#$iterator]).
Vnější `{foreach}` seskupí položky do pro každou kategorii a pak nad nimi iteruje vnitřní `{foreach}`.

Kdyby podmínka byla splněná vždy, tak se ve vnitřním cyklu vykreslí všechny prvky:

```latte
{foreach $items as $item}
Expand All @@ -84,47 +89,115 @@ Výsledek bude vypadat takto:
</ul>
```

K čemu je takové použití iterateWhile dobré? Jak se liší od řešení, které jsme si ukázali úplně na začátku tohoto návodu? Rozdíl je v tom, že když bude tabulka prázdná a nebude obsahovat žádné prvky, nevypíše se prázdné `<ul></ul>`.

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

Řešení bez `{iterateWhile}`
---------------------------

Pokud bychom stejný úkol řešili zcela základními prostředky šablonovacích systému, například v Twig, Blade, nebo čistém PHP, vypadalo by řešení cca takto:
Vnořené smyčky
--------------

V rámci jednoho cyklu můžeme vytvářet více vnitřních smyček a dokonce je zanořovat. Takto by se daly seskupovat třeba podkategorie atd.

Dejme tomu, že v tabulce bude ještě další sloupec `subcategoryId` a kromě toho, že každá kategorie bude v samostatném `<ul>`, každá každý podkategorie samostatném `<ol>`:

```latte
{var $prevCategoryId = null}
{foreach $items as $item}
{if $item->categoryId !== $prevCategoryId}
{* změnila se kategorie *}
{foreach ($items|group: categoryId) as $items1}
<ul>
{foreach ($items1|group: subcategoryId) as $items2}
<ol>
{foreach $items2 as $item}
<li>{$item->name}
{/foreach}
</ol>
{/foreach}
</ul>
{/foreach}
```

{* uzavřeme předchozí <ul>, pokud nejde o první položku *}
{if $prevCategoryId !== null}
</ul>
{/if}

{* otevřeme nový seznam *}
<ul>
Filtr |batch
------------

{do $prevCategoryId = $item->categoryId}
{/if}
Seskupování lineárních položek obstarává také filtr `batch`, a to do dávek s pevným počtem prvků:

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

Lze jej nahradit za iterateWhile tímto způsobem:

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

{if $prevCategoryId !== null}
{* uzavřeme poslední seznam *}

Značka `{iterateWhile}`
-----------------------

Stejné úkoly, jako jsme řešili s filtrem `|group`, si ukážeme s použitím značky `{iterateWhile}`. Hlavní rozdíl mezi oběma přístupy je v tom, že `group` nejprve zpracuje a seskupí všechny vstupní data, zatímco `{iterateWhile}` řídí průběhu cyklů s podmínkami, takže iterace probíhá postupně.

Nejprve vykreslíme tabulku s kategoriemi pomocí iterateWhile:

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

Tento kód je však nesrozumitelný a neintuitivní. Není vůbec jasná vazba mezi otevíracími a zavíracími HTML značkami. Není na první pohled vidět, jestli tam není nějaká chyba. A vyžaduje pomocné proměnné jako `$prevCategoryId`.
Zatímco `{foreach}` označuje vnější část cyklu, tedy vykreslování seznamů pro každou kategorii, tak značka `{iterateWhile}` označuje vnitřní část, tedy jednotlivé položky.
Podmínka v koncové značce říká, že opakování bude probíhat do té doby, dokud aktuální i následující prvek patří do stejné kategorie (`$iterator->nextValue` je [následující položka|/tags#$iterator]).

Oproti tomu řešení s `{iterateWhile}` je čisté, přehledné, nepotřebujeme pomocné proměnné a je blbuvzdorné.
Kdyby podmínka byla splněná vždy, tak se ve vnitřním cyklu vykreslí všechny prvky:

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

Podmínka v otevírací značce
---------------------------
Výsledek bude vypadat takto:

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

K čemu je takové použití iterateWhile dobré? Jak se liší od řešení, které jsme si ukázali úplně na začátku tohoto návodu? Rozdíl je v tom, že když bude tabulka prázdná a nebude obsahovat žádné prvky, nevypíše se prázdné `<ul></ul>`.

Pokud uvedeme podmínku v otevírací značce `{iterateWhile}`, tak se chování změní: podmínka (a přechod na další prvek) se vykoná už na začátku vnitřního cyklu, nikoliv na konci.
Tedy zatímco do `{iterateWhile}` bez podmínky se vstoupí vždy, do `{iterateWhile $cond}` jen při splnění podmínky `$cond`. A zároveň se s tím do `$item` zapíše následující prvek.
Expand Down Expand Up @@ -161,10 +234,6 @@ Původní kód upravíme tak, že nejprve vykreslíme první položku a poté ve
{/foreach}
```


Vnořené smyčky
--------------

V rámci jednoho cyklu můžeme vytvářet více vnitřních smyček a dokonce je zanořovat. Takto by se daly seskupovat třeba podkategorie atd.

Dejme tomu, že v tabulce bude ještě další sloupec `subcategoryId` a kromě toho, že každá kategorie bude v samostatném `<ul>`, každá každý podkategorie samostatném `<ol>`:
Expand All @@ -184,31 +253,4 @@ Dejme tomu, že v tabulce bude ještě další sloupec `subcategoryId` a kromě
```


Filtr |batch
------------

Seskupování lineárních položek obstarává také filtr `batch`, a to do dávek s pevným počtem prvků:

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

Lze jej nahradit za iterateWhile tímto způsobem:

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

{{leftbar: /@left-menu}}

0 comments on commit c8faa54

Please sign in to comment.