Š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/ └── UI/ └── 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/ └── UI/ ├── @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 tak, že je zapíšeme do $this->template
a potom je máme k dispozici
v šabloně jako lokální proměnné:
$this->template->article = $this->articles->getById($id);
Takto jednoduše můžeme do šablon předat jakékoliv proměnné. Při vývoji robustních aplikací ale bývá užitečnější se omezit. Například tak, že explicitně nadefinujeme výčet proměnných, které šablona očekává, a jejich typů. Díky tomu nám bude moci PHP kontrolovat typy, IDE správně našeptávat a statická analýza odhalovat chyby.
A jak takový výčet nadefinujeme? Jednoduše v podobě třídy a její properties. 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
. Takže PHP bude
při zápisu kontrolovat deklarované typy. A počínaje verzí PHP 8.2 upozorní i na zápis do neexistující proměnné,
v předchozích verzích lze téhož dosáhnout použitím traity Nette\SmartObject.
Anotace @property-read
je určená pro IDE a statickou analýzu, díky ní bude fungovat našeptávání, viz PhpStorm and code completion for
$this->template.
Luxusu našeptávání si můžete dopřát i v šablonách, stačí do PhpStorm nainstalovat plugin pro Latte a uvést na začátek šablony název třídy, více v článku Latte: jak na typový systém:
{templateType App\UI\Article\ArticleTemplate}
...
Takto fungují i šablony v komponentách, stačí jen dodržet jmennou konvenci a pro komponentu např.
FifteenControl
vytvořit třídu šablony FifteenTemplate
.
Pokud potřebujete vytvořit $template
jako instanci jiné třídy, využijte metodu
createTemplate()
:
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Výchozí proměnné
Presentery a komponenty předávají do šablon několik užitečných proměnných automaticky:
$basePath
je absolutní URL cesta ke kořenovému adresáři (např./eshop
)$baseUrl
je absolutní URL ke kořenovému adresáři (např.http://localhost/eshop
)$user
je objekt reprezentující uživatele$presenter
je aktuální presenter$control
je aktuální komponenta nebo presenter$flashes
pole 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.
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 apod. Lze tak učinit přímo v metodě
render<View>
nebo beforeRender()
:
public function beforeRender(): void
{
// přidání filtru
$this->template->addFilter('foo', /* ... */);
// nebo konfigurujeme přímo objekt Latte\Engine
$latte = $this->template->getLatte();
$latte->addFilterLoader(/* ... */);
}
Latte ve verzi 3 nabízí pokročilejší způsob a to vytvoření si extension pro každý webový projekt. Kusý příklad takové třídy:
namespace App\UI\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()),
// ...
];
}
// ...
}
Zaregistrujeme ji pomocí konfigurace:
latte:
extensions:
- App\UI\Accessory\LatteExtension
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.