Latte makra

Přehled a popis všech maker šablonovacího systému Latte, které jsou vám standardně k dispozici.

Výpis proměnných a výrazů
{$variable} vypíše escapovanou proměnnou
{$variable|noescape} vypíše proměnnou bez escapování
{expression} vypíše escapovaný výraz
{expression|noescape} vypíše výraz bez escapování
Podmínky
{if $cond} … {elseif $cond} … {else} … {/if} podmínka if
{$cond ? $value1 : $value2} ternární operátor
{$cond ? $value} zkrácený „ternární“ operátor
{$a ?: $default} vypíše $a, pokud je prázdné tak $default
{ifset $var} … {elseifset $var} … {/ifset} podmínka if (isset())
{switch $var} … {case value} … {default} … {/switch} volba podle hodnoty
Cykly
{foreach $arr as $item} … {/foreach} cyklus foreach
{for expr; expr; expr} … {/for} cyklus for
{while expr} … {/while} cyklus while
{continueIf $cond} podmíněný skok na další iteraci
{breakIf $cond} podmíněné ukončení cyklu
{first [$mod]} … {/first} vypsat při prvním průchodu
{last [$mod]} … {/last} vypsat při posledním průchodu
{sep} … {/sep} separátor
Proměnné
{var $foo = value} vytvoří proměnnou
{default $foo = value} výchozí proměnnou pokud neexistuje
{capture $var} … {/capture} zachytí blok do proměnné
Ostatní
{spaceless} … {/spaceless} odstraní nadbytečné mezery, stejně jako filtr strip
{include 'file.latte'} načte šablonu z dalšího souboru
{php expression} vyhodnotí výraz, ale nevypíše
{* text komentáře *} komentář, bude odstraněn
{syntax mode} změna syntaxe maker za běhu
{l} nebo {r} vypíše znak { nebo }
{contentType $type} přepne escapování a pošle HTTP hlavičku
{debugbreak $cond} umístí do kódu breakpoint
Pomocí HTML kodéra
n:class chytrý zápis HTML atributu class
n:attr chytrý zápis jakéhokoliv HTML atributu
n:ifcontent Vynechá prázdný HTML tag
n:tag-if Vynechá HTML tag, pokud je podmínka false
Bloky, layouty, dědičnost šablon
{block block} definuje a hned vykreslí blok
{define block} definuje blok pro pozdější použití
{include block} vloží blok
{import 'file.latte'} načte bloky z externí šablony
{layout 'file.latte'} určuje soubor s layoutem
{extends 'file.latte'} alias pro {layout}
{ifset #block} … {/ifset} podmínka, zda existuje blok

Dostupné pouze v Nette Frameworku

Odkazy
n:href odkaz používaný v HTML elementech <a>
{link Presenter:action} vygeneruje odkaz
{plink Presenter:action} vygeneruje odkaz na presenter
{ifCurrent $link} … {/ifCurrent} deprecated, používejte n:class=„$presenter->linkCurrent ? …“
Ovládací prvky a formuláře
{control loginForm} vykreslí komponentu
{form formName} … {/form} vykreslí značky formuláře
{label fieldName} … {/label} vykreslí popisku formulářového prvku
{input fieldName} vykreslí formulářový prvek
{inputError fieldName} vypíše chybovou hlášku formulářového prvku
n:name oživí formulářový prvek
AJAX
{snippet name} … {/snippet} výstřižek, který lze odeslat AJAXem
{snippetArea name} obálka pro výstřižky
Překlady
{_}Text{/_} přeloží text
{_expression} přeloží výraz a vypíše s escapováním
Ostatní
{dump $variable} dumpuje proměnné do Debugger Bar
{cache $key} … {/cache} cachuje část šablony
Operátory
(expand) rozbalení polí

Vypsání proměnné nebo výrazu {expression}

S escapováním: (eliminuje riziko Cross Site Scripting):

Jméno: {$name} {$surname}<br>
Věk: {date('Y') - $birth}<br>

Bez escapování uvedením modifikátoru |noescape (dříve se používal vykříčník):

<div class="article">
    {$content|noescape}
    {nl2br($notes)|noescape}
</div>

Alternativně lze použit zápis s rovnítkem {=expression}.

Díky technologií Context-Aware Escaping je mimo jiné možné nativně používat PHP proměnné uvnitř JavaScriptu:

<script>
var pole = {$arr};
var name = {$name}; // pozor, zapisuje se bez uvozovek!
</script>

Podmínky {if $cond} … {/if}, {$cond ? … : …}

Podmínky se chovají stejně, jako jejich protějšky v PHP:

{if $stock}
    Skladem
{elseif $onWay}
    Na cestě
{else}
    Není dostupné
{/if}

Lze použít i makro {ifset}, která kontroluje existenci proměnné a odpovídá zápisu if (isset($var)). Makro {ifset} lze také použít pro ověření existence bloků.

Výraz v podmínce {if} lze uvést také v ukončovací značce, což se hodí v situacích, kdy při otevírání podmínky ještě jeho výsledek neznáme:

{if}
    <h1>Výpis řádků z databáze</h1>

    {foreach $database->table as $row} ... {/foreach}
{/if $row}

Blok {if} s podmínkou v ukončovací značce nepodporuje makro {elseif}. Makro {else} použít lze.

dostupnost: {$stock ? 'Skladem' : 'Není dostupné'}

zkráceně: {$stock ? 'Skladem'}

Opakující se výraz podmínky lze někdy zkrátit makry {switch} a {case}. Ty se přeloží na sérii podmínek if ($var === 'value'), tedy fungují trochu jinak než v PHP. break se neuvádí.

{switch $transport}
    {case train}
        Vlakem
    {case plane}
        Letecky
    {default}
        Jinak
{/switch}

Cykly {foreach}, {for}, {while}

Cykly foreach, for a while se chovají stejně, jako jejich protějšky v PHP.

{foreach $result as $row}
    <span>{$row->title}</span>
{/foreach}
{while $row = $result->fetch()}
    <span>{$row->title}</span>
{/while}
{for $i = 0; $i < 10; $i++}
    <span>Položka {$i}</span>
{/for}

Uvnitř cyklu foreach je inicializovaná proměnná $iterator, díky které můžeme zjišťovat užitečné informace o probíhajícím cyklu. Její API disponuje čítačem průchodů a metodami zjišťujícími, zda je aktuální průchod sudý, lichý, první nebo poslední:

  • isFirst() – prochází se cyklem poprvé?
  • isLast() – jde o poslední průchod?
  • getCounter() – čítač průchodů cyklem počítaný od jedničky
  • isOdd() – jde o lichý průchod?
  • isEven() – jde o sudý průchod?

Objekt $iteratorvlastnosti traity Nette\SmartObject, proto lze místo $iterator->isFirst() psát $iterator->first a podobně. Příklad:

{foreach $rows as $row}
    {if $iterator->first}<table>{/if}

    <tr id="row-{$iterator->counter}">
        <td>{$row->name}</td>
        <td>{$row->email}</td>
    </tr>

    {if $iterator->last}</table>{/if}
{/foreach}

Konstrukci {if $iterator->first}<table>{/if} lze nahradit za ekvivalentní {first}<table>{/first}, obdobně podmínku s last lze zapsat jako {last}</table>{/last}. Makra {first} a {last} mají nepovinný parametr, který lze využít jako modulo.

{foreach $rows as $row}
    {first}<table>{/first}
        {first 5}<tr>{/first} {* nový řádek po 5 záznamech *}
            <td>{$row->value}</td>
        {last 5}</tr>{/last}
    {last}</table>{/last}
{/foreach}

A nakonec, ekvivalentem {if !$iterator->last} … {/if} (tj. neposlední prvek) je makro {sep} … {/sep}. Obvykle jim označíme oddělovač, například čárku mezi vypisovanými položkami, čímž zajistíme, že se nevypíše přebývající pravostranná čárka:

{foreach $items as $item} {$item} {sep}, {/sep} {/foreach}

Uvnitř cyklu lze používat makra {continueIf ?} a {breakIf ?}, které přejdou na další prvek resp. ukončí cyklus při splnění podmínky:

{foreach $rows as $row}
    {continueIf $row->parent == null}
    ...
{/foreach}

Definici cyklu lze sloučit s definicí obalujícího prvku HTML následujícím způsobem: (vypíše pro každou řádku tag div a jeho vnitřek)

<div n:foreach="$rows as $row">
    ...
</div>

Nebo pomocí vnitřního cyklu opakovat pouze vnitřek tagu: (ul zobrazí jednou, li pro každou řádku)

<ul n:inner-foreach="$rows as $row">
  <li>
    ...
  </li>
</ul>

Deklarace proměnných {var} a {default}

Proměnné můžeme deklarovat přímo v šabloně makrem {var}:

{var $name = 'John Smith'}
{var $age = 27}

{* Vícenásobná deklarace *}
{var $name = 'John Smith', $age = 27}

Makro {default} funguje podobně s tím rozdílem, že nastavuje hodnoty jen neexistujícím proměnným:

{default $lang = 'cs'}

Zachytávání do proměnné {capture}

Makrem {capture} lze zachytit výstup do proměnné:

{capture $var}
<ul>
        <li>Hello World</li>
</ul>
{/capture}

<p>Captured: {$var}</p>

Pokud chcete zachytit výstup jen proto, abyste na něj mohli aplikovat nějaký filtr, je jednodušší filtr aplikovat přímo na blok:

{block|strip}
<ul>
        <li>Hello World</li>
</ul>
{/block}

Vložení souboru {include}

Do šablony lze vložit jinou šablonu. Vložený soubor má k dispozici globální proměnné aktuální šablony a případné další parametry, které mu při volání předáme:

{include 'basket.latte'}

{include 'menu.latte', level => 3, data => $menu}

Makro {include} se kromě vkládání souborů používá i pro vkládání bloků.

Vyhodnocení kódu {php expression}

Vyhodnotí kód a nic nevypisuje.

Hlavička {contentType}

Přepne Context-Aware Escaping do kontextu určeného argumentem a všechny proměnné se escapují podle pravidel tohoto kontextu. Například {contentType xml} přepne do režimu XML, {contentType text} escapování zcela vypne.

Pokud je parametrem plnohodnotný MIME type, tak se navíc odešle jako HTTP hlavička do prohlížeče:

{contentType application/xml}
<?xml version="1.0"?>
<rss version="2.0">
    <channel>
        <title>RSS feed</title>
        <item>
            ...
        </item>
    </channel>
</rss>

Break point {debugbreak}

Určuje místo, kde dojde k zastavení vykonávání programu. Používá se při ladění, aby mohl programátor provést inspekci běhového prostředí a zjistil, zda program funguje podle očekávání. Podporován je Xdebug a PhpEd. Lze doplnit podmínku, která určuje, kdy má být program zastaven.

{debugbreak} {* Zastaví program *}

{debugbreak $counter == 1}  {* Zastaví program při splnění podmínky *}

Změna syntaxe {syntax}

Makra nemusejí být uzavřena výhradně do jednoduchých složených závorek, můžeme si zvolit i jiný oddělovač a to dokonce za běhu. Slouží k tomu makro {syntax …}, kde jako parametr lze uvést:

  • latte: {...}
  • double: {{...}}
  • asp: <%...%>
  • python: {% ... %} a {{ ... }}
  • off: vypne zpracování maker

S využitím n:maker lze vypnout Latte třeba jen jednomu bloku JavaScriptu:

<script n:syntax="off">
    var obj = {var: 123}; // tohle neni makro
</script>

Latte je pohodlně použitelné i v JavaScriptu, jen se stačí vyhnout konstrukci uvedené v předchozím příkladu. Buď zápisem mezery za otevírací složenou závorku { var: 123} nebo použitím uvozovek kolem identifikátoru {'var': 123}.

Pokud Latte vypnete pomocí {syntax off} (tj. makrem syntax, nikoliv n:makrem), mějte na paměti, že ho již zpátky nemůžete zapnout pomocí {/syntax}. Žádné složené závorky se nezpracovávají, tudíž ani tag pro ukončení výjimky v syntaxi.

n:class

Potřebujeme vyřešit běžný úkol: vypisujeme data z databáze jako elementy <li>, neznáme dopředu jejich počet a chceme, aby každý lichý řádek měl třídu alt a poslední třídu last. Pokud by byl poslední řádek lichý, má mít obě třídy. A protože jsme puntičkáři, nechceme v kódu žádné prázdné <li class=" "> apod.

Díky makru n:class tohle zapíšeme neskutečně snadno:

<ul>
    {foreach $data as $item}
    <li n:class="$iterator->odd ? alt, $iterator->last ? last">{$item}</li>
    {/foreach}
</ul>

Anebo potřebujete označit aktivní menu třídou current?

<a href="#" n:class="$presenter->isLinkCurrent('Presenter:*') ? current">

Úžasné, že?

Pokud potřebujete přesnější detekci URL, tak metodě isLinkCurrent() lze předat i očekávaný parametr URL

<a href="#" n:class="$presenter->isLinkCurrent('Presenter:action', 'slug') ? current">

n:attr

Makro n:attr umí s elegancí podobnou makru n:class generovat libovolné HTML atributy. Hodí se především v případech, kdy nevíme, které atributy budeme chtít vypsat a které ne.

<input type="checkbox" n:attr="value => $value, checked => $checked">

Vypíše v závislosti na hodnotách proměnných $value nebo $checked:

<input type="checkbox">

{* $value = 'Hello' *}
<input type="checkbox" value="Hello">

{* $checked = true *}
<input type="checkbox" value="Hello" checked>

n:ifcontent

n:ifcontent macro odstraní prázdný tag

<div>
    <div class="error" n:ifcontent>{$error}</div>
</div>

Vypíše v závislosti na hodnotě proměnné $error:

{* $error = '' *}
<div>
</div>

{* $error = 'Required' *}
<div>
    <div class="error">Required</div>
</div>

Bloky {block} a {define}

Bloky slouží pro označení libovolné oblasti šablony. Třeba proto, abychom na ni aplikovali volání filtru (viz příklad) nebo proto, abychom ji pojmenovali a mohli poté na jiném místě znovu vložit.

{block sidebar}
    <h3>Menu</h3>
    ...
{/block}

Bloky lze zanořovat do sebe. A pochopitelně je můžeme zapsat prostřednictvím n:makra:

{block sidebar}
    <h3>Menu</h3>
    ...

    <ul n:block="menulist">
        ...
    </ul>
{/block}

Blok se na svém místě vykreslí. Pokud bychom chtěli pojmenovaný blok jen definovat bez vykreslení, použijeme místo makra {block} makro {define}.

Název bloku může být také určen proměnnou: {block $var} … {/block}.

Každý blok vidí proměnné ze svého okolí. Avšak vytvoření nebo změna hodnoty proměnné uvnitř bloku se projeví pouze v něm (a pochopitelně i ve vnořených blocích).

Existenci bloku můžeme ověřit makrem {ifset #sidebar}.

Vkládání bloků {include block}

Vložení bloku zajistí makro {include}.

{include sidebar}

Při vkládání bloku je možné mu předat parametry:

{include sidebar, id => 123, name => $value}

Můžeme vložit i blok, jehož název je uložen v proměnné (abychom se odlišili od vkládání souboru, uvedeme před názvem bloku mřížku):

{include #$block$name}

V bloku lze vložit i sebe sama, což lze použít např. pro vykreslení stromového menu.

{block menu}
<ul>
    {foreach $menu as $item}
    <li>{if is_array($item)}
        {include menu, menu => $item}
    {else}
        {$item}
    {/if}</li>
    {/foreach}
</ul>
{/block}

Místo {include menu, ...} lze psát také {include this, ...}.

Pokud je potřeba doplnit již definovaný blok (např. přidání nových stylů), není kvůli tomu potřeba kopírovat celý obsah tohoto bloku. Místo toho si nadefinujeme nový (stejně pojmenovaný) blok a uvnitř zavoláme {include parent}.

{block styles}
    {include parent}
    <link href="http://url.to.styles.css" rel="stylesheet">
{/block}

Rozšiřování a dědičnost šablon {layout}

Rozšiřování šablon (též dědičnost šablon) představuje mocný nástroj, který umožňuje snadnou a efektivní tvorbu i velmi komplikovaných layoutů bez nutnosti zbytečného opakování kódu.

Mějme dvě jednoduché stránky. První:

<html>
    <head>
        <title>První stránka | Můj web</title>
    </head>
    <body>
        <div id="sidebar">
            <ul>...</ul>
        </div>

        <div id="content">
            <p>Lorem Gypsum...</p>
        </div>
    </body>
</html>

A druhou:

<html>
    <head>
        <title>Druhá stránka | Můj web</title>
    </head>
    <body>
        <div id="sidebar">
            <ul>...</ul>
        </div>

        <div id="content">
            <p>Proin eu sem purus. Donec bibendum
            vestibulum...</p>
        </div>
    </body>
</html>

Stránky se liší pouze elementem <title> a <div#content>. Vytvoříme si proto layout, ve kterém budou všechny společné části, a vytvoříme tzv. placeholdery. Což kupodivu nebude nic jiného, než klasický blok:

Soubor @layout.latte:

<html>
    <head>
        <title>{block title}{/block} | Můj web</title>
    </head>
    <body>
        <div id="sidebar">
            <ul>...</ul>
        </div>

        <div id="content">{block content}{/block}</div>
    </body>
</html>

V jednotlivých šablonách se nyní na layout odkážeme makrem {layout} a můžeme je redukovat na ony dva bloky:

{layout '@layout.latte'}

{block title}První stránka{/block}

{block content}<p>Lorem Gypsum...</p>{/block}

Obdobně bude vypadat i stránka druhá.

Nejsme dokonce ani omezeni dvoustupňovým layoutem. Vrstev může být totiž tolik, kolik se nám hodí.

Vykreslování komponent {control …}

Značka {control} slouží k vykreslování komponent presenteru. Pro lepší pochopení je dobré vědět, jak se tato značka přeloží do PHP.

{control cartControl}       pro celý košík na stránce
{control cartControl:small} pro malý náhledový košík

se přeloží jako:

$control->getComponent('cartControl')->render();
$control->getComponent('cartControl')->renderSmall();

Metoda getComponent() vrací komponentu cartControl a nad touto komponentou volá metodu render(), resp. renderSmall() pokud je jiný způsob renderování uveden ve značce za dvojtečkou.

Lze použít i volání s parametry, které se předají render metodám, například:

{control cartControl, $maxItems, $showSummary}
{control cartControl, maxItems => 5, showSummary => true}
{control cartControl, [maxItems => 5, showSummary => true]}
{control cartControl:small, $maxItems, $showSummary}

se přeloží jako:

$control->getComponent('cartControl')->render($maxItems, $showSummary);
$control->getComponent('cartControl')->render(['maxItems' => 5, 'showSummary' => true]);
$control->getComponent('cartControl')->render([['maxItems' => 5, 'showSummary' => true]]);
$control->getComponent('cartControl')->renderSmall($maxItems, $showSummary);

Pokud se kdekoliv v parametrech objeví =>, všechny parametry budou zabaleny do pole a předány jako první argument.

Vykreslení sub-komponety:

{control cartControl-someForm}

se přeloží jako:

$control->getComponent("cartControl-someForm")->render();

Formuláře {form}, {input}, {inputError}{label}

Makra usnadňují vykreslení formulářů a jejich prvků:

{form myForm}
<table>
<tr n:foreach="$form->controls as $name => $field">
    <th>{label $name /}<th>
    <td>{input $name}</td>
    <td n:ifcontent>{inputError $name}</td>
</tr>
</table>
{/form}

Makro {input} vykreslí jakýkoliv prvek, včetně select boxů nebo textarea. Makro {label} může být volitelně párové, např. {label}Věk{/label}, a ohraničený text bude u vícejazyčných formulářů také přeložen. Pokud formulářový prvek obsahuje chybu, makro {inputError} ji vypíše.

Pokud potřebuje formuláře naroubovat na existující HTML kód, můžeme využít makro n:name, které zaktivní existující prvky v šabloně:

<form n:name=myForm>
    <p>Jméno: <input size="50" n:name="name"></p>
    <p>Heslo: <input size="30" n:name="password"></p>
</form>

Překlady {_expression}

Makro usnadňuje automatizované překlady v šablonách. Pro jejich správnou funkčnost musí být nastaven překladač, viz lokalizace:

$latte->addFilter('translate', new MyTranslator);

Poté bude jakýkoliv výraz zapsaný v tomto makru automaticky přeložen:

{_$variable}

Pro textové části šablony lze použít párovou variantu:

{_}Text, který bude přeložen{/_}

Dumpování proměnných {dump}

Vyžaduje Tracy.

{dump $name} {* Vypíše proměnnou $name *}

{dump}       {* Vypíše všechny aktuálně definované proměnné *}

Operátor rozbalení pole (expand)

Operátor (expand) umožňuje použití pole v místě, kde se očekává více argumentů. Jde o obdobu operátoru ... z PHP, na rozdíl od něj zachovává i klíče.

Lze jej použít například pro předání argumentů do bloků nebo vkládaných šablon, pokud je máme v poli.

{include 'foobar.latte' (expand) $args}     {* argumenty pro include makro *}