Șabloane
Nette utilizează sistemul de șabloane Latte. Pe de o parte, pentru că este cel mai sigur sistem de șabloane pentru PHP și, pe de altă parte, și cel mai intuitiv sistem. Nu trebuie să învățați multe lucruri noi, vă descurcați cu cunoștințele de PHP și câteva tag-uri.
Este obișnuit ca pagina să fie compusă dintr-un șablon de layout + șablonul acțiunii respective. Așa poate arăta, de
exemplu, un șablon de layout, observați blocurile {block}
și tag-ul {include}
:
<!DOCTYPE html>
<html>
<head>
<title>{block title}My App{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
Și acesta va fi șablonul acțiunii:
{block title}Homepage{/block}
{block content}
<h1>Homepage</h1>
...
{/block}
Acesta definește blocul content
, care se va insera în locul {include content}
din layout, și, de
asemenea, re-definește blocul title
, care va suprascrie {block title}
din layout. Încercați să vă
imaginați rezultatul.
Căutarea șabloanelor
Nu trebuie să specificați în presentere ce șablon trebuie redat, framework-ul deduce singur calea și vă scutește de scris.
Dacă utilizați o structură de directoare unde fiecare presenter are propriul director, plasați pur și simplu șablonul
în acest director sub numele acțiunii (resp. view), adică pentru acțiunea default
utilizați șablonul
default.latte
:
app/ └── Presentation/ └── Home/ ├── HomePresenter.php └── default.latte
Dacă utilizați o structură unde presenterele sunt împreună într-un singur director și șabloanele în folderul
templates
, salvați-l fie în fișierul <Presenter>.<view>.latte
, fie
<Presenter>/<view>.latte
:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── Home.default.latte ← prima variantă └── Home/ └── default.latte ← a doua variantă
Directorul templates
poate fi plasat și cu un nivel mai sus, adică la același nivel cu directorul cu clasele
presenterelor.
Dacă șablonul nu este găsit, presenterul răspunde cu eroarea 404 – page not found.
View-ul îl schimbați folosind $this->setView('jineView')
. De asemenea, se poate specifica direct fișierul cu
șablonul folosind $this->template->setFile('/path/to/template.latte')
.
Fișierele unde se caută șabloanele pot fi modificate prin suprascrierea metodei formatTemplateFiles(), care returnează un array de nume posibile de fișiere.
Căutarea șablonului de layout
Nette caută automat și fișierul cu layout-ul.
Dacă utilizați o structură de directoare unde fiecare presenter are propriul director, plasați layout-ul fie în folderul cu presenterul, dacă este specific doar pentru el, fie cu un nivel mai sus, dacă este comun pentru mai mulți presenteri:
app/ └── Presentation/ ├── @layout.latte ← layout comun └── Home/ ├── @layout.latte ← doar pentru presenterul Home ├── HomePresenter.php └── default.latte
Dacă utilizați o structură unde presenterele sunt împreună într-un singur director și șabloanele în folderul
templates
, layout-ul va fi așteptat în aceste locații:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── @layout.latte ← layout comun ├── Home.@layout.latte ← doar pentru Home, prima variantă └── Home/ └── @layout.latte ← doar pentru Home, a doua variantă
Dacă presenterul se află într-un modul, se va căuta și la niveluri de directoare superioare, în funcție de imbricarea modulului.
Numele layout-ului poate fi schimbat folosind $this->setLayout('layoutAdmin')
și atunci se va aștepta în
fișierul @layoutAdmin.latte
. De asemenea, se poate specifica direct fișierul cu șablonul layout-ului folosind
$this->setLayout('/path/to/template.latte')
.
Folosind $this->setLayout(false)
sau tag-ul {layout none}
în interiorul șablonului, căutarea
layout-ului se dezactivează.
Fișierele unde se caută șabloanele de layout pot fi modificate prin suprascrierea metodei formatLayoutTemplateFiles(), care returnează un array de nume posibile de fișiere.
Variabile în șablon
Variabilele le transmitem șablonului scriindu-le în $this->template
și apoi le avem disponibile în șablon
ca variabile locale:
$this->template->article = $this->articles->getById($id);
Astfel de simplu putem transmite șabloanelor orice variabile. Însă, la dezvoltarea aplicațiilor robuste, este mai util să ne limităm. De exemplu, prin definirea explicită a listei de variabile pe care șablonul le așteaptă și a tipurilor lor. Datorită acestui fapt, PHP ne va putea verifica tipurile, IDE-ul ne va sugera corect și analiza statică va dezvălui erorile.
Și cum definim o astfel de listă? Simplu, sub forma unei clase și a proprietăților sale. O numim similar cu presenterul,
doar cu Template
la sfârșit:
/**
* @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;
// și alte variabile
}
Obiectul $this->template
din presenter va fi acum o instanță a clasei ArticleTemplate
. Deci,
PHP va verifica tipurile declarate la scriere. Și începând cu versiunea PHP 8.2 va avertiza și la scrierea
într-o variabilă inexistentă, în versiunile anterioare se poate obține același lucru folosind trait-ul Nette\SmartObject.
Adnotarea @property-read
este destinată IDE-ului și analizei statice, datorită ei va funcționa sugerarea, vezi
PhpStorm and code completion for
$this->template.

De luxul sugerării vă puteți bucura și în șabloane, este suficient să instalați în PhpStorm plugin-ul pentru Latte și să specificați la începutul șablonului numele clasei, mai multe în articolul Latte: cum să folosiți sistemul de tipuri:
{templateType App\Presentation\Article\ArticleTemplate}
...
Astfel funcționează și șabloanele în componente, este suficient doar să respectați convenția de nume și pentru
componenta, de ex. FifteenControl
, să creați clasa șablonului FifteenTemplate
.
Dacă aveți nevoie să creați $template
ca instanță a altei clase, utilizați metoda
createTemplate()
:
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Variabile implicite
Presenterele și componentele transmit automat către șabloane câteva variabile utile:
$basePath
este calea URL absolută către directorul rădăcină (de ex./eshop
)$baseUrl
este URL-ul absolut către directorul rădăcină (de ex.http://localhost/eshop
)$user
este obiectul reprezentând utilizatorul$presenter
este presenterul curent$control
este componenta sau presenterul curent$flashes
array de mesaje trimise de funcțiaflashMessage()
Dacă utilizați propria clasă de șablon, aceste variabile se transmit dacă creați proprietăți pentru ele.
Crearea linkurilor
În șablon se creează linkuri către alți presenteri & acțiuni în acest mod:
<a n:href="Product:show">detaliu produs</a>
Atributul n:href
este foarte util pentru tag-urile HTML <a>
. Dacă dorim să afișăm linkul
în altă parte, de exemplu în text, folosim {link}
:
Adresa este: {link Home:default}
Mai multe informații găsiți în capitolul Crearea linkurilor URL.
Filtre personalizate, tag-uri etc.
Sistemul de șabloane Latte poate fi extins cu filtre, funcții, tag-uri etc. personalizate. Acest lucru se poate face direct
în metoda render<View>
sau beforeRender()
:
public function beforeRender(): void
{
// adăugarea unui filtru
$this->template->addFilter('foo', /* ... */);
// sau configurăm direct obiectul Latte\Engine
$latte = $this->template->getLatte();
$latte->addFilterLoader(/* ... */);
}
Latte în versiunea 3 oferă o modalitate mai avansată și anume crearea unei extensii pentru fiecare proiect web. Un exemplu fragmentar al unei astfel de clase:
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()),
// ...
];
}
// ...
}
O înregistrăm folosind configurația:
latte:
extensions:
- App\Presentation\Accessory\LatteExtension
Traducere
Dacă programați o aplicație multilingvă, probabil veți avea nevoie să afișați unele texte din șablon în diferite
limbi. Nette Framework definește în acest scop o interfață pentru traducere Nette\Localization\Translator, care are
o singură metodă translate()
. Aceasta primește mesajul $message
, care de obicei este un șir de
caractere, și orice alți parametri. Sarcina este de a returna șirul tradus. În Nette nu există nicio implementare implicită,
puteți alege în funcție de nevoile dvs. dintre mai multe soluții gata făcute, pe care le găsiți pe Componette. În documentația lor veți afla cum să configurați
translatorul.
Șabloanelor li se poate seta un traducător, pe care îl primim transmis, prin metoda
setTranslator()
:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
Translatorul poate fi setat alternativ folosind configurația:
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
Apoi, traducătorul poate fi utilizat, de exemplu, ca filtru |translate
, inclusiv cu parametri suplimentari, care
sunt transmiși metodei translate()
(vezi foo, bar
):
<a href="basket">{='Coș'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
Sau ca tag cu underscore:
<a href="basket">{_'Coș'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Pentru traducerea unei secțiuni a șablonului există un tag pereche {translate}
(de la Latte 2.11, anterior se
folosea tag-ul {_}
):
<a href="order">{translate}Comandă{/translate}</a>
<a href="order">{translate foo, bar}Comandă{/translate}</a>
Translatorul este apelat standard în timpul rulării la redarea șablonului. Latte versiunea 3, însă, poate traduce toate textele statice deja în timpul compilării șablonului. Astfel se economisește performanță, deoarece fiecare șir se traduce o singură dată și traducerea rezultată se scrie în forma compilată. În directorul cu cache se creează astfel mai multe versiuni compilate ale șablonului, una pentru fiecare limbă. Pentru aceasta este suficient doar să specificați limba ca al doilea parametru:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator, $lang);
}
Prin text static se înțelege, de exemplu, {_'hello'}
sau {translate}hello{/translate}
. Textele
nestatice, cum ar fi {_$foo}
, se vor traduce în continuare în timpul rulării.