Pět novinek v Latte 3.1, které vám zpříjemní život

před 3 hodinami od David Grudl  

Latte 3.1 přináší pětici novinek – nové filtry |column, |commas a |limit, vylepšený |slice pro iterátory a dvě nové featury enginu. Žádná revoluce, ale drobnosti, které oceníte.

Konec úniku proměnných z {foreach}

Tohle mě štvalo roky. Napíšete {foreach $items as $item}, cyklus skončí, a $item vám vesele přežívá s hodnotou posledního prvku. Horší je, když si tím přepíšete proměnnou, se kterou jste počítali:

{var $name = 'Jupí'}

{foreach $users as $name}
    ...
{/foreach}

{$name} {* ouha, tady už není 'Jupí' *}

Latte vás sice odedávna varovalo, když foreach přepsal proměnnou předanou do šablony přes parametry. Ale proměnné vytvořené přímo v šabloně, například přes {var}, se přepisovaly tiše. A to je přesně ten zákeřný případ.

Teď to jde vyřešit nastavením:

$latte->setFeature(Latte\Feature::ScopedLoopVariables);

Proměnné z {foreach} od teď existují jen uvnitř cyklu. Po skončení se vrátí původní hodnota. A pokud proměnná předtím neexistovala? Zmizí úplně.

{var $name = 'Jupí'}

{foreach $users as $name}
    ...
{/foreach}

{$name} {* vypíše 'Jupí', jupí! *}

A to staré varování o přepsaných parametrech? S touhle featurou se vypíná, protože přestává dávat smysl – nic se už nepřepisuje.

Funguje to i s destrukturováním {foreach $data as [$a, $b]} a samozřejmě i s klíčem {foreach $arr as $k => $v} – po cyklu se uklidí vše. Vnořené cykly mají nezávislé scope.

Jediná výjimka: pokud iterujete přes referenci {foreach $arr as &$v}, scope se neuplatní – reference přímo modifikují původní pole, takže obnovování hodnot po cyklu by je rozbilo. Logické.

Odsazujte šablony, jak chcete

Znáte to: máte <ul> a uvnitř {foreach}, který generuje <li>. Přirozeně to odsadíte, aby šablona byla čitelná. Jenže to odsazení se pak propíše do výstupu. Buď máte hezký kód a ošklivý HTML, nebo naopak.

Neakceptovatelné.

S Feature::Dedent tohle dilema mizí:

$latte->setFeature(Latte\Feature::Dedent);

Latte automaticky odstraní společné odsazení uvnitř párových tagů. Takže tohle:

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

vygeneruje:

<ul>
    <li>...</li>
    <li>...</li>
</ul>

Odsazení jako by tam nikdy nebylo.

A protože se dedent provádí už při kompilaci šablony, ne při každém renderování, nemá to žádný vliv na výkon. Funguje pro všechny párové tagy – {if}, {block}, {capture}, {foreach} a další. Vnořené tagy se dedentují nezávisle, každý na své úrovni.

Pokud je odsazení nekonzistentní – kupříkladu mícháte tabulátory s mezerami, nebo některý řádek nemá dostatečné odsazení – Latte vyhodí CompileException s přesným číslem řádku. Žádné tiché polykání chyb.

Filtr |commas

Spojit pole do řetězce odděleného čárkami – brnkačka. Ale co když chcete před posledním prvkem napsat „a" místo čárky? To je přesně ta věc, kvůli které v šabloně začnete psát podmínky a najednou máte čtyři řádky kódu místo jednoho. Přitom chcete říct jen „jablko, hruška a švestka".

{['jablko', 'hruška', 'švestka']|commas}       {* jablko, hruška, švestka *}
{['jablko', 'hruška', 'švestka']|commas:' a '}  {* jablko, hruška a švestka *}
{['jablko', 'hruška', 'švestka']|commas:', nebo '}  {* jablko, hruška, nebo švestka *}

Bez parametru spojí čárkou a mezerou. S parametrem použije zadaný řetězec jako oddělovač mezi posledními dvěma prvky – zbytek zůstane oddělený čárkami. Prostě čeština, ne foreach.

Filtr |column

Máte pole záznamů o uživatelích, třeba [['id' => 1, 'name' => 'Jan'], ['id' => 2, 'name' => 'Petr'], ...], a potřebujete z nich dostat jen jména? Filtr |column vytáhne hodnoty jednoho sloupce – ať už jde o klíč v poli, nebo property objektu:

{$users|column:'name'|commas}   {* Jan, Petr, Marie *}

Volitelně přijímá i druhý parametr pro indexování výsledků:

{foreach ($users|column:'name':'id') as $id => $name}
    {$id}: {$name}
{/foreach}

Funguje i s iterátory, nejen s poli – nicméně iterátor se interně převede na pole, takže na žádnou lazy magii nečekejte.

Filtr |slice pro iterátory a nový filtr |limit

Filtr |slice vyřízne kus pole nebo řetězce (u řetězců s respektem k UTF-8). Dosud ale fungoval jen s poli a řetězci. Teď zvládne i iterátory a generátory – vrací generátor, který čte prvky z původního zdroje jeden po druhém a po dosažení limitu se zastaví. Celý iterátor se do paměti nenačítá:

{foreach ($generator|slice:0:10) as $item}
    {$item}
{/foreach}

A k tomu přibyl filtr |limit – pohodlnější varianta pro typický případ „vezmi prvních N prvků". Funguje s poli, iterátory i řetězci (s respektem k UTF-8):

{foreach ($items|limit:5) as $item}
    {$item}
{/foreach}

{$description|limit:100}

Rozdíl oproti |slice: filtr |limit ve výchozím stavu zachovává originální klíče.

Jak na to

Nové featury ScopedLoopVariables a Dedent se zapínají přes setFeature():

$latte = new Latte\Engine;
$latte->setFeature(Latte\Feature::ScopedLoopVariables);
$latte->setFeature(Latte\Feature::Dedent);

A pokud používáte Nette, stačí to zapnout v konfiguraci:

latte:
    scopedLoopVariables: true
    dedent: true

Pět drobností pro život.

David Grudl Tvůrce open-source projektů a specialista na AI, který lidem otevírá dveře do světa umělé inteligence. Jeho projekty Nette a další používají weby, které denně navštěvujete. Píše na Uměligence, La Trine a moderuje Tech Guys. Organizuje AI workshopy a věří, že technologie mají smysl jen tehdy, když lidem skutečně pomohou.