Latte 2.9: to nejlepší nakonec
Nová verze přináší funkce, které změní způsob práce se šablonami. Jejich kódování bude mnohem zábavnější, než jste si kdy mysleli.
Letos již byly vydány tři klíčové verze Latte:
- Latte 2.6 s volitelným zřetězením a uživatelskými funkcemi
- Latte 2.7: typy kam se podíváš a batch
- Latte 2.8 přinášející opevnění uvnitř šablony
Ale je to nová verze 2.9, která přináší nejvíce nových věcí. Pojďme se na ně podívat!
{foreach}
+ {else}
Značka {foreach}
může mít volitelnou klauzuli
{else}
, jejíž text se zobrazí, pokud je pole kterým iterujeme
prázdné:
<ul>
{foreach $people as $person}
<li>{$person->name}</li>
{else}
<li><em>Litujeme, v tomto seznamu nejsou žádní uživatelé</em></li>
{/foreach}
</ul>
{skipIf}
Je velmi podobný {continueIf}
, ale neinkremetuje počítadlo.
Při výpisu $iterator->counter
a přeskočení některých
položek tedy v číslování nejsou žádné díry. A co je
nejdůležitější, klauzule {else}
bude vykreslena, když
přeskočíte všechny položky. Cool!
<ul>
{foreach $people as $person}
{skipIf $person->age < 18}
<li>{$iterator->counter}. {$person->name}</li>
{else}
<li><em>Litujeme, v tomto seznamu nejsou žádní dospělí uživatelé</em></li>
{/foreach}
</ul>
$iterátor
: counter0 &
parent
Byly přidány dvě nové vlastnosti objektu $iterator
.
Vlastnost $iterator->counter0
, sourozenec čítače
$iterator->counter
, ale počítaná od nuly.
A vlastnost $iterator->parent
, která vrací iterátor
obklopující aktuální.
{try}
& {else}
& {rollback}
Úžasná nová funkce, díky níž je extrémně snadné vytvářet
robustní šablony. Pokud při vykreslování bloku {try}
dojde
k výjimce, celý blok bude přeskočen a vykreslování bude pokračovat až
po něm:
{try}
<ul>
{foreach $twitter->loadTweets() as $tweet}
<li>{$tweet->text}</li>
{/foreach}
</ul>
{/try}
Volitelná klauzule {else}
je vykreslena při výjimce:
{try}
<ul>
{foreach $twitter->loadTweets() as $tweet}
<li>{$tweet->text}</li>
{/foreach}
</ul>
{else}
<p>Sorry, unable to load tweets.</p>
{/try}
Blok {try}
lze také zastavit a přeskočit ručně pomocí
{rollback}
. Nemusíte tak předem kontrolovat všechna vstupní
data a během vykreslování se můžete rozhodnout, zda má smysl objekt
vykreslit:
{try}
<ul>
{foreach $people as $person}
{skipIf $person->age < 18}
<li>{$person->name}</li>
{else}
{rollback}
{/foreach}
</ul>
{/try}
Je také možné definovat vlastní obslužný handler výjimek, třeba pro logování:
$latte = new Latte\Engine;
$latte->setExceptionHandler(function (\Throwable $e) {
...
});
{ifchanged}
Zkontrolujte, zda se hodnota změnila od poslední iterace ve smyčce (for, foreach, while).
Pokud je ve značce předána jedna nebo více proměnných, zkontroluje, zda se nějaká proměnná změnila. Například následující příklad vytiskne nadpis s prvním písmenem pokaždé, když se změní při výpisu jmen:
{foreach ($names|sort) as $name}
{ifchanged $name[0]} <h2>{$name[0]}</h2> {/ifchanged}
<p>{$name}</p>
{/foreach}
Pokud není zadán žádný argument, zkontroluje vykreslený obsah oproti jeho předchozímu stavu a zobrazí jej pouze pokud se změnil. V předchozím příkladu tedy můžeme argument vynechat. Můžeme také použít n:atribut:
{foreach ($names|sort) as $name}
<h2 n:ifchanged>{$name[0]}</h2>
<p>{$name}</p>
{/foreach}
Značka {ifchanged}
může mít také volitelnou klauzuli
{else}
, která se zobrazí, pokud se hodnota nezmění.
{embed}
Game-changer v dědičnosti bloků.
Embed je kombinace mezi dědičností {extends}
a vložením
{include}
. Umožní vám vložit obsah jiné šablony a zároveň
přepsat jakýkoli blok definovaný uvnitř zahrnuté šablony, jako při
dědění šablon.
{embed 'article.latte', social: ['facebook', 'twitter', 'instagram']}
{* tyto bloky definované v article.latte přepíšeme právě zde *}
{block title}
<div>Header Content</div>
{/block}
{block main}
<div>Main Content</div>
{/block}
{/embed}
Šablona article.latte
:
<div class="article-component">
<header>{block title}Default Header Content{/block}</header>
<main>{block main}Default Main Content{/block}</main>
<footer>{block footer}Default Footer Content{/block}</footer>
{ifset $social}
<ul class="social">
<li n:foreach="$social as $item">{$item}</li>
</ul>
{/ifset}
</div>
Lokální bloky
Bloky jsou sdíleny mezi více šablonami, pokud jsou součástí hierarchie
dědičnosti ({layout}
) nebo pokud je importujeme
({import}
). Někdy však v takových šablonách se hodí
vytvořit blok (prostřednictvím {block}
nebo
{define}
), který by neměl být k dispozici v jiných
šablonách, ani by neměl přepisovat další bloky, pokud se jejich názvy
shodují.
Řešením je označit takový blok jako lokální pomocí slova
local
před jménem:
{block local tree}
This is local block
{/block}
{case 1, 2, 3}
Klauzule {case}
nyní může obsahovat více hodnot:
{switch $status}
{case $status::NEW}<b>new item</b>
{case $status::SOLD, $status::UNKNOWN}<i>not available</i>
{/switch}
Protože {switch}
v Latte používá striktní srovnání a
nepotřebuje break
, je nyní přesným ekvivalentem pro
match
z PHP 8.
{include block / file}
& {ifset block}
Tag {include}
lze použít k vložení bloků nebo souborů.
Podobně lze {ifset}
použít k testování existence proměnné
nebo bloku. Latte pozná, že jde o blok, pokud se argument skládá pouze
z alfanumerických znaků.
Nyní je možné rozlišit, že se jedná o blok resp. soubor, a to
výslovným uvedením klíčového slova block
resp.
file
, které je užitečné například v případě, že je
název uložen v proměnné.
{include block $blockName}
{include file $file}
{ifset block $blockName} ... {/ifset}
{include block from file}
Nyní můžete pomocí značky {include}
vložit pouze jediný
blok ze souboru. Funguje také pro lokální bloky.
{include sidebar from 'template.latte'}
{include file with blocks}
Přímou náhradou za deprecated značku {includeblock}
, která
měla nevhodný název, je právě tato značka:
`{include 'template.latte' with blocks}`
Výchozí hodnoty v
{define}
Parametry v {define}
mohou mít výchozí hodnoty. Pokud
žádnou neuvedete, je tak jako doposud null
.
{define sidebar $class, $cols = 2}
...
{/define}
Filtr a funkce clamp
Historicky se jedná o první výchozí vlastní funkci v Latte. Vrátí hodnotu omezenou na inkluzivní rozsah min a max.
{=clamp($level, 0, 255)}
{$level|clamp: 0, 255}
Filtr |sort
Filtr, který jednoduše seřadí pole:
{foreach ($names|sort) as $name}
...
{/foreach}
Pole seřazené v obráceném pořadí:
{foreach ($names|sort|reverse) as $name}
...
{/foreach}
Vliv PHP 8.0: pojmenované argumenty
Pro pojmenované argumenty ve značkách jako {include}
nebo
{link}
můžete použít syntaxi podobnou PHP 8:
before
{include 'file.latte' arg1 => 1, arg2 => 2}
now
{include 'file.latte' arg1: 1, arg2: 2}
before
{link default page => 1}
now
{link default page: 1}
Navíc, pokud používáte PHP 8, můžete použít pojmenované argumenty
uvnitř všech volání funkcí, jako je {=func(a: 10, b: 20)}
Z důvodu budoucí podpory pojmenovaných argumentů v modifikátorech je
použití dvojtečky jako oddělovače argumentů označeno za deprecated.
Místo dvojtečky použijte čárku: {$var|filter: a: 2}
⇒ {$var|filter: a, 2}
Vliv PHP 8.0: operátory
?->
a ??->
Latte přišlo s funkcí volitelného řetězení téměř před rokem. PHP 8 nyní přichází s něčím opravdu velmi podobným pod názvem nullsafe operator:
$var?->prop?->elem[1]?->call()?->item
Chování však není úplně stejné. Chování v PHP 8 generuje
varování, pokud proměnná, vlastnost nebo index pole neexistuje (v uvedeném
příkladu, pokud $var
, $var->prop
nebo
elem[1]
neexistuje). Pouze testuje, zda je hodnota rovna null.
Naproti tomu chování v Latte je podobné null coalescing
operátoru ??
. To znamená, že neexistující proměnná,
vlastnost nebo index pole se nepovažují za chybu.
Latte 2.9 sjednocuje chování a operátor ?->
se nyní
chová stejně jako v PHP, aby byl konzistentní. Ale předchozí chování
nyní implementuje nový undefined-safe operátor
??->
:
$var??->prop??->elem[1]??->call()
Je vůbec ještě něco, co v Latte chybí?
Komentáře
Něco by mě přeci jen napadlo :)
Občas by se hodilo něco, čím by šlo nějak šikovně měnit tag na základě podmínky – třeba v šabloně komponenty na výpis novinek na HP jsou nadpisy v
<h3>
(protože má nad sebou nadpis „Novinky“), ale na /novinky/ už v<h2>
.Další příklad by mohla být drobečkovka, kdy jednotlivé názvy drobků jsou v
<a>
, ale jen ten poslední je<span>
a nemá tedy ani href.Dnes se to dá udělat třeba přes n:tag-if, ale to duplikuje na tagu např. třídu a případné další atributy. Nebo třeba v šabloně pracovat s Html objektem poslaným z komponenty, kde se to vše pořeší – ale to se tak trošku zase tahá HTML zpět do PHP.
Popravdě mě ale zatím vůbec nenapadá, jak by šla taková věc řešit nějak elegantně a IDE/editory si u toho nestěžovaly…
Každopádně díky za vychytávky v nové verzi! :)
#1 Gappa nenapadá mě nic přehlednějšího, než ty dva tagy s
n:tag-if
. Samozřejmě některé atributy se duplikují, jenže jiné zase ne (třebahref
), takže nevím jak to udělat jinak. Každopádně lze zapsat i něco takového:#2 David Grudl To jsem také už někdy použil, ale to spadá do kategorie „nechutná to IDE“ – a moc přehledné to bohužel také není :)
Nejlépe z toho asi fakt vychází
:n-tag-if
a tu třídu si dát případně do proměnné.Super novinky a vychytávky. Díky za ně. Ještě mě mě napadá když už je sort, tak zrovna včera jsem potřeboval ksort.
$iterátor: counter0 & parent
Funguje to tedy rekurzivně? $iterator->parent->parent
Dobrá práce (y)
Davide, Tebe je vždy radost si poslechnout či přečíst, díky.
Geniální, foreach-else mi úplně vyrazilo dech, to člověk používá docela často. Díky!
Jsem fakt v šoku, to je boží! Jsem si říkal tak po třetí novince, že to je úplně krutý a určitě to už bude všechno… no a pak přišla čtvrtá, pátá… je toho fakt moc a všechno se zdá být supercool. Díky za to, jdu přemýšlet, jak své projekty co nejdřív povýšit, abych tyhle fíčury mohl začít hned používat a šablony si tak zase o chlup zpřehlednil 🙂
Btw: Na drobky používám taky to n:tag-if, taky se mi to moc nelíbí, ale taky mne nenapadá, jak to řešit líp.
…napadla mne jedna věc, která mi v poslední době v Latte trochu chybí.
Mám rád n:tagy, tzn. často používám třeba n:foreach nebo n:if atd. V takovém případě ale nemůžu puožít else větev.
Nebyl by dobrý nápad přidat nový pseudo-html-latte-tag
<else>
(nebo<else>
), který by umožňoval využít else větev i pro n:tagy?…a tady diskuse mi zasahuje do zprávy 🙂 To „(nebo
<else>
)“ obsahuje mezeru a lomítko před ukončující ostrou závorkou.#10 Eda je to v plánu, ale protože to vyžaduje upravit interní věci, bude to ve verzi 3.0
Topový, díky. Už se těším 🙂
Ale kurde, zas jsem nevymyslel nic, co bys neměl v plánu… Budu muset zabrat 😁
Super :) Hezké by bylo, kdyby šlo taky
{control myComponent}
{block b1}přepisovat{/block}
{block b2}bloky{/block}
{block b3}i v reálných komponentách{/block}
{/control}
ale nejspíš by to z logiky věci nešlo udělat nebo…?
#9 Eda Ve verzi 2.10 je vyřešené takto, takže super! :)
#2 David Grudl Nedávno jsem to taky řešil. Napadlo mě něco jako:
{foreach $breadcrumbs as $element}
<a n:tag="$iterator->isLast() ? span : null" class="breadcrumb-element" n:href="Page:detail $element->id">
{$element->name}</a>
{/foreach}
Přičemž latte tag ‚tag‘ by mohl dokázat rozlišit, jaké prvky jsou povoleny, takže ve výchozím stavu (výsledek podmínky === null) by se vždy vykresloval prvek, který má všechny definované atributy a podle toho, jaké atributy má povolené prvek, kterým se bude nahrazovat, tak by se vykreslily jen ty povolené. Tedy v HTML je jasně dáno, že span nemá atribut href, tak poslední prvek, který se vykreslí jako span bude mít href atribut odfiltrován. Prostě se nepropíše.
Zkoušel jsem si i takové makro udělat, ale nakonec jsem skončil u toho, že jsem použil dva samostatné HTML tagy, jeden A s HREF a jeden SPAN bez HREF
Chcete-li odeslat komentář, přihlaste se