Šablony
Nette používá šablonovací systém Latte. Jednak proto, že jde o nejlépe zabezpečený šablonovací systém pro PHP, a zároveň také systém nejintuitivnější. Nemusíte se učit mnoho nového, vystačíte si se znalostí PHP a několika značek.
Je obvyklé, že stránka se složí ze šablony layoutu + šablony dané akce. Takhle třeba může vypadat šablona layoutu,
všimněte si bloků {block} a značky {include}:
<!DOCTYPE html>
<html>
<head>
<title>{block title}My App{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
A tohle bude šablona akce:
{block title}Homepage{/block}
{block content}
<h1>Homepage</h1>
...
{/block}
Ta definuje blok content, který se vloží na místo {include content} v layoutu, a také
re-definuje blok title, kterým přepíše {block title} v layoutu. Zkuste si představit
výsledek.
Hledání šablon
Nemusíte v presenterech uvádět, jaká šablona se má vykreslit, framework cestu odvodí sám a ušetří vám psaní.
Pokud používáte adresářovou strukturu, kde každý presenter má vlastní adresář, jednodušše umístěte šablonu do
tohoto adresáře pod jménem akce (resp. view), tj. pro akci default použijte šablonu
default.latte:
app/
└── Presentation/
└── Home/
├── HomePresenter.php
└── default.latte
Pokud používáte strukturu, kde jsou společně presentery v jednom adresáři a šablony ve složce templates,
uložte ji buď do souboru <Presenter>.<view>.latte nebo
<Presenter>/<view>.latte:
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── Home.default.latte ← 1. varianta
└── Home/
└── default.latte ← 2. varianta
Adresář templates může být umístěn také o úroveň výš, tj. na stejné úrovni, jako je adresář
s třídami presenterů.
Pokud se šablona nenajde, presenter odpoví chybou 404 – page not found.
View změníte pomocí $this->setView('jineView'). Také lze přímo určit soubor se šablonou pomocí
$this->template->setFile('/path/to/template.latte').
Soubory, kde se dohledávají šablony, lze změnit překrytím metody formatTemplateFiles(), která vrací pole možných názvů souborů.
Hledání šablony layoutu
Nette také automaticky dohledává soubor s layoutem.
Pokud používáte adresářovou strukturu, kde každý presenter má vlastní adresář, umístěte layout buď do složky s presenterem, pokud je specifický jen pro něj, nebo o úroveň výš, pokud je společný pro více presenterů:
app/
└── Presentation/
├── @layout.latte ← společný layout
└── Home/
├── @layout.latte ← jen pro presenter Home
├── HomePresenter.php
└── default.latte
Pokud používáte strukturu, kde jsou společně presentery v jednom adresáři a šablony ve složce templates,
bude se layout očekávat na těchto místech:
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── @layout.latte ← společný layout
├── Home.@layout.latte ← jen pro Home, 1. varianta
└── Home/
└── @layout.latte ← jen pro Home, 2. varianta
Pokud se presenter nachází v modulu, bude se dohledávat i o další adresářové úrovně výš, podle zanoření modulu.
Název layoutu lze změnit pomocí $this->setLayout('layoutAdmin') a pak se bude očekávat v souboru
@layoutAdmin.latte. Také lze přímo určit soubor se šablonou layoutu pomocí
$this->setLayout('/path/to/template.latte').
Pomocí $this->setLayout(false) nebo značky {layout none} uvnitř šablony se dohledávání
layoutu vypne.
Soubory, kde se dohledávají šablony layoutu, lze změnit překrytím metody formatLayoutTemplateFiles(), která vrací pole možných názvů souborů.
Proměnné v šabloně
Proměnné do šablony předáváme zápisem do $this->template. V šabloně jsou pak dostupné jako lokální
proměnné:
$this->template->article = $this->articles->getById($id);
Pokud chcete, aby se hodnota určité property automaticky předala do šablony jako proměnná, označte
ji atributem #[TemplateVariable] a viditelností public:
use Nette\Application\Attributes\TemplateVariable;
class ArticlePresenter extends Nette\Application\UI\Presenter
{
#[TemplateVariable]
public string $siteName = 'Můj blog';
}
Výchozí proměnné
Presentery a komponenty předávají do šablon několik užitečných proměnných automaticky:
$basePathje absolutní URL cesta ke kořenovému adresáři (např./eshop)$baseUrlje absolutní URL ke kořenovému adresáři (např.http://localhost/eshop)$userje objekt reprezentující uživatele$presenterje aktuální presenter$controlje aktuální komponenta nebo presenter$flashespole zpráv zaslaných funkcíflashMessage()
Pokud používáte vlastní třídu šablony, tyto proměnné se předají, pokud pro ně vytvoříte property.
Typově bezpečné šablony
Při vývoji robustních aplikací je užitečné explicitně nadefinovat, jaké proměnné šablona očekává a jakého jsou typu. Získáte tak typovou kontrolu v PHP, chytré našeptávání v IDE a schopnost statické analýzy odhalovat chyby.
Jak takový výčet nadefinovat? Jednoduše v podobě třídy s properties reprezentujícími proměnné šablony.
Pojmenujeme ji podobně jako presenter, jen s Template na konci:
/**
* @property-read ArticleTemplate $template
*/
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
public Model\Article $article;
public Nette\Security\User $user;
// a další proměnné
}
Objekt $this->template v presenteru bude nyní instancí třídy ArticleTemplate. PHP tak bude
při zápisu kontrolovat deklarované typy.
Anotace @property-read slouží pro IDE a statickou analýzu, díky ní bude fungovat našeptávání, viz PhpStorm and code completion for
$this->template.

Našeptávání můžete využít i přímo v šablonách. Stačí do PhpStorm nainstalovat plugin pro Latte a uvést na začátek šablony název třídy parametrů šablony, více v článku Latte: jak na typový systém:
{templateType App\Presentation\Article\ArticleTemplate}
...
Totéž platí i pro komponenty. Stačí dodržet jmennou konvenci a pro komponentu např. FifteenControl
vytvořit třídu parametrů FifteenTemplate.
Pokud potřebujete použít jinou třídu parametrů, využijte metodu createTemplate():
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Vytváření odkazů
V šabloně se vytvářejí odkazy na další presentery & akce tímto způsobem:
<a n:href="Product:show">detail produktu</a>
Atribut n:href je velmi šikovný pro HTML značky <a>. Chceme-li odkaz vypsat jinde,
například v textu, použijeme {link}:
Adresa je: {link Home:default}
Více informací najdete v kapitole Vytváření odkazů URL.
Vlastní filtry, značky apod.
Šablonovací systém Latte lze rozšířit o vlastní filtry, funkce, značky a další prvky. K dispozici jsou tři způsoby, jak to udělat, od nejrychlejších ad-hoc řešení až po architektonický přístup pro celou aplikaci.
Ad-hoc v metodách presenteru
Nejrychlejší způsob je přidat filtr nebo funkci přímo v kódu presenteru či komponenty. V presenteru je k tomu
vhodná metoda beforeRender() nebo render<View>():
protected function beforeRender(): void
{
// přidání filtru
$this->template->addFilter('money', fn($val) => round($val) . ' Kč');
// přidání funkce
$this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6);
}
V šabloně pak:
<p>Cena: {$price|money}</p>
{if isWeekend($now)} ... {/if}
Pro složitější logiku můžete konfigurovat přímo objekt Latte\Engine:
protected function beforeRender(): void
{
$latte = $this->template->getLatte();
$latte->setMigrationWarnings();
}
Pomocí atributů
Elegantní způsob je definovat filtry a funkce jako metody přímo ve třídě parametrů šablony presenteru nebo komponenty a označit je atributy:
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
#[Latte\Attributes\TemplateFilter]
public function money(float $val): string
{
return round($val) . ' Kč';
}
#[Latte\Attributes\TemplateFunction]
public function isWeekend(DateTimeInterface $date): bool
{
return $date->format('N') >= 6
}
}
Latte automaticky rozpozná a zaregistruje metody označené těmito atributy. Název filtru nebo funkce v šabloně odpovídá názvu metody. Tyto metody nesmí být privátní.
Globálně pomocí Extension
Předchozí způsoby jsou vhodné pro filtry a funkce, které potřebujete jen v konkrétním presenteru nebo komponentě, nikoliv v celé aplikaci. Pro celou aplikaci je nejvhodnější vytvořit si extension. Jde o třídu, která centralizuje všechna rozšíření Latte pro celý projekt. Kusý příklad:
namespace App\Presentation\Accessory;
final class LatteExtension extends Latte\Extension
{
public function __construct(
private App\Model\Facade $facade,
private Nette\Security\User $user,
// ...
) {
}
public function getFilters(): array
{
return [
'timeAgoInWords' => $this->filterTimeAgoInWords(...),
'money' => $this->filterMoney(...),
// ...
];
}
public function getFunctions(): array
{
return [
'canEditArticle' =>
fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
// ...
];
}
private function filterTimeAgoInWords(DateTimeInterface $time): string
{
// ...
}
// ...
}
Extension zaregistrujeme pomocí konfigurace:
latte:
extensions:
- App\Presentation\Accessory\LatteExtension
Výhodou extension je, že lze využít dependency injection, mít přístup k modelové vrstvě aplikace a všechna rozšíření mít přehledně na jednom místě. Extension umožnuje definovat i vlastní značky, providery, průchody pro Latte kompilátor a další.
Překládání
Pokud programujete vícejazyčnou aplikaci, budete nejspíš potřebovat některé texty v šabloně vypsat v různých
jazycích. Nette Framework k tomuto účelu definuje rozhraní pro překlad Nette\Localization\Translator, které má jedinou
metodu translate(). Ta přijímá zprávu $message, což zpravidla bývá řetězec, a libovolné
další parametry. Úkolem je vrátit přeložený řetězec. V Nette není žádná výchozí implementace, můžete si vybrat
podle svých potřeb z několika hotových řešeních, které najdete na Componette. V jejich dokumentaci se dozvíte, jak translator
konfigurovat.
Šablonám lze nastavit překladač, který si necháme předat, metodou
setTranslator():
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
Translator je alternativně možné nastavit pomocí konfigurace:
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
Poté lze překladač používat například jako filtr |translate, a to včetně doplňujících parametrů,
které se předají metodě translate() (viz foo, bar):
<a href="basket">{='Košík'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
Nebo jako podtržítkovou značku:
<a href="basket">{_'Košík'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Pro překlad úseku šablony existuje párová značka {translate} (od Latte 2.11, dříve se používala značka
{_}):
<a href="order">{translate}Objednávka{/translate}</a>
<a href="order">{translate foo, bar}Objednávka{/translate}</a>
Translator se standardně volá za běhu při vykreslování šablony. Latte verze 3 ovšem umí všechny statické texty překládat už během kompilace šablony. Tím se ušetří výkon, protože každý řetězec se přeloží jen jednou a výsledný překlad se zapíše do zkompilované podoby. V adresáři s cache tak vznikne více zkompilovaných verzí šablony, jedna pro každý jazyk. K tomu stačí pouze uvést jazyk jako druhý parametr:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator, $lang);
}
Statickým textem je myšleno třeba {_'hello'} nebo {translate}hello{/translate}. Nestatické texty,
jako třeba {_$foo}, se nadále budou překládat za běhu.