Latte 2.9: to nejlepší nakonec

před 4 lety od David Grudl  

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:

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

  1. Je vůbec ještě něco, co v Latte chybí?

    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! :)

    před 4 lety · replied [2] David Grudl
  2. #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řeba href), takže nevím jak to udělat jinak. Každopádně lze zapsat i něco takového:

    <{if $cond}a href="...."{else}span{/if} class=link>...</{if $cond}a{else}span{/if}>
    
    před 4 lety · replied [3] Gappa [16] Polki
  3. #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é.

    před 4 lety
  4. 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.

    před 4 lety
  5. $iterátor: counter0 & parent
    Funguje to tedy rekurzivně? $iterator->parent->parent

    před 4 lety
  6. Dobrá práce (y)

    před 4 lety
  7. Davide, Tebe je vždy radost si poslechnout či přečíst, díky.

    před 4 lety
  8. Geniální, foreach-else mi úplně vyrazilo dech, to člověk používá docela často. Díky!

    před 4 lety
  9. 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.

    před 4 lety · replied [15] Gappa
  10. …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?

    před 4 lety · replied [12] David Grudl
  11. …a tady diskuse mi zasahuje do zprávy 🙂 To „(nebo <else>)“ obsahuje mezeru a lomítko před ukončující ostrou závorkou.

    před 4 lety
  12. #10 Eda je to v plánu, ale protože to vyžaduje upravit interní věci, bude to ve verzi 3.0

    před 4 lety
  13. Topový, díky. Už se těším 🙂

    Ale kurde, zas jsem nevymyslel nic, co bys neměl v plánu… Budu muset zabrat 😁

    před 4 lety
  14. 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…?

    před 4 lety
  15. #9 Eda Ve verzi 2.10 je vyřešené takto, takže super! :)

    před 4 lety
  16. #2 David Grudl Nedávno jsem to taky řešil. Napadlo mě něco jako:

    {foreach $breadcrumbs as $element}
    <a n:tag="$iterator-&gt;isLast() ? span : null" class="breadcrumb-element" n:href="Page:detail $element-&gt;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

    před 3 lety

Chcete-li odeslat komentář, přihlaste se