Template
Nette usa il sistema di template Latte. Latte viene usato perché è il sistema di template più sicuro per PHP e allo stesso tempo il più intuitivo. Non dovete imparare molto di nuovo: bastano la conoscenza di PHP e qualche tag.
È consuetudine che una pagina sia composta dal template del layout più il template dell'azione specifica. Ecco che aspetto
potrebbe avere un template di layout; notate i blocchi {block} e il tag {include}:
<!DOCTYPE html>
<html>
<head>
<title>{block title}My App{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
E questo sarebbe il template dell'azione:
{block title}Homepage{/block}
{block content}
<h1>Homepage</h1>
...
{/block}
Definisce il blocco content, che viene inserito al posto di {include content} nel layout, e
ridefinisce inoltre il blocco title, che sovrascrive {block title} del layout. Provate a immaginare il
risultato.
Ricerca dei template
Nei presenter non dovete indicare quale template va disegnato: il framework ne deduce automaticamente il percorso, risparmiandovi di scriverlo.
Se usate una struttura di directory in cui ogni presenter ha la propria directory, basta collocare il template in questa
directory con il nome dell'azione (cioè della vista). Per esempio, per l'azione default usate il template
default.latte:
app/
└── Presentation/
└── Home/
├── HomePresenter.php
└── default.latte
Se usate una struttura in cui i presenter stanno insieme in un'unica directory e i template in una cartella
templates, salvatelo o nel file <Presenter>.<vista>.latte oppure in
<Presenter>/<vista>.latte:
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── Home/
│ └── default.latte ← 1ª variante
└── Home.default.latte ← 2ª variante
La directory templates si può collocare anche un livello più in alto, cioè allo stesso livello della directory
con le classi dei presenter.
Se il template non viene trovato, il presenter risponde con un errore 404 – pagina non trovata.
Potete cambiare la vista con $this->setView('otherView'). È anche possibile indicare direttamente il file del
template con $this->template->setFile('/path/to/template.latte').
I file in cui vengono cercati i template si possono cambiare sovrascrivendo il metodo formatTemplateFiles(), che restituisce un array dei possibili nomi di file.
Ricerca del template di layout
Nette cerca automaticamente anche il file del layout.
Se usate una struttura di directory in cui ogni presenter ha la propria directory, collocate il layout o nella cartella del presenter, se è specifico solo di quello, oppure un livello più in alto, se è comune a più presenter:
app/
└── Presentation/
├── @layout.latte ← layout comune
└── Home/
├── @layout.latte ← solo per il presenter Home
├── HomePresenter.php
└── default.latte
Se usate una struttura in cui i presenter sono raggruppati in un'unica directory e i template stanno in una cartella
templates, il layout sarà atteso in queste posizioni:
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── @layout.latte ← layout comune
├── Home/
│ └── @layout.latte ← solo per Home, 1ª variante
└── Home.@layout.latte ← solo per Home, 2ª variante
Se il presenter si trova in un modulo, la ricerca prosegue anche verso i livelli superiori di directory, secondo l'annidamento dei moduli.
Il nome del layout si può cambiare con $this->setLayout('layoutAdmin'), e allora sarà atteso nel file
@layoutAdmin.latte. Potete anche indicare direttamente il file del template di layout con
$this->setLayout('/path/to/template.latte').
Con $this->setLayout(false) oppure con il tag {layout none} dentro il template si disattiva la
ricerca del layout.
I file in cui vengono cercati i template di layout si possono cambiare sovrascrivendo il metodo formatLayoutTemplateFiles(), che restituisce un array dei possibili nomi di file.
Variabili del template
Le variabili si passano ai template scrivendole in $this->template. Diventano poi disponibili nel template come
variabili locali:
$this->template->article = $this->articles->getById($id);
Per passare automaticamente al template il valore di una proprietà come variabile, contrassegnatela con
l'attributo #[TemplateVariable] e con visibilità pubblica:
use Nette\Application\Attributes\TemplateVariable;
class ArticlePresenter extends Nette\Application\UI\Presenter
{
#[TemplateVariable]
public string $siteName = 'My blog';
}
Se passate al template una variabile con lo stesso nome, #[TemplateVariable] non la sovrascriverà.
Variabili predefinite
I presenter e i componenti passano automaticamente ai template alcune variabili utili:
$basePathè il percorso URL assoluto della directory radice (per esempio/eshop)$baseUrlè l'URL assoluto della directory radice (per esempiohttp://localhost/eshop)$userè un oggetto che rappresenta l'utente$presenterè il presenter corrente$controlè il componente o il presenter corrente$flashesè un array dei messaggi inviati dalla funzioneflashMessage()
Se usate una classe di template personalizzata, queste variabili vengono passate se create per esse una proprietà.
Template con tipi sicuri
Sviluppando applicazioni solide, è utile definire esplicitamente quali variabili il template si aspetta e di che tipo sono. Questo offre il controllo dei tipi in PHP, suggerimenti intelligenti nell'IDE e permette all'analisi statica di individuare gli errori.
Come si definisce un elenco del genere? Semplicemente come una classe con proprietà che rappresentano le variabili del
template. Chiamatela come il presenter, aggiungendo alla fine Template:
/**
* @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;
// e altre variabili
}
L'oggetto $this->template nel presenter sarà ora un'istanza della classe ArticleTemplate. PHP
controllerà quindi i tipi dichiarati durante la scrittura.
Nette sceglie automaticamente la classe del template. Cerca prima una classe chiamata
<Presenter><Azione>Template, per esempio ArticleEditTemplate per l'azione edit,
e solo se non esiste ripiega su <Presenter>Template.
L'annotazione @property-read è destinata all'IDE e all'analisi statica e abilita il completamento del codice,
vedi PhpStorm e il completamento del codice per
$this->template.

Potete usare il completamento del codice anche direttamente nei template. Basta installare il plugin Latte per PhpStorm e indicare all'inizio del template il nome della classe dei parametri, maggiori informazioni nel capitolo Latte: sistema di tipi:
{templateType App\Presentation\Article\ArticleTemplate}
...
Lo stesso vale per i componenti. Basta seguire la convenzione di denominazione e creare una classe dei parametri
FifteenTemplate per un componente come FifteenControl.
Se vi serve usare una classe dei parametri diversa, usate il metodo createTemplate():
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Se vi serve influire su come il template viene completato prima del rendering, per esempio per aggiungere
variabili condivise da tutte le azioni, potete sovrascrivere nel presenter il metodo completeTemplate(). Viene
chiamato subito prima che il template venga disegnato:
protected function completeTemplate(Nette\Application\UI\Template $template): void
{
parent::completeTemplate($template);
$template->siteName = 'My blog';
}
Creare i link
Nel template i link verso altri presenter e azioni si creano così:
<a n:href="Product:show">dettaglio del prodotto</a>
L'attributo n:href è molto comodo per i tag HTML <a>. Se vogliamo stampare il link altrove,
per esempio nel testo, usiamo {link}:
L'URL è: {link Home:default}
Maggiori informazioni si trovano nel capitolo Creazione di link URL.
Filtri, tag e altro personalizzati
Il sistema di template Latte si può estendere con filtri, funzioni, tag e altri elementi personalizzati. Ci sono tre approcci disponibili, che vanno dalle soluzioni rapide ad hoc ai modelli architetturali per intere applicazioni.
Ad hoc nei metodi del presenter
L'approccio più rapido è aggiungere filtri o funzioni direttamente nel codice del presenter o del componente. Nei presenter
si prestano bene i metodi beforeRender() o render<Vista>():
protected function beforeRender(): void
{
// aggiunta di un filtro
$this->template->addFilter('money', fn($val) => '$' . number_format($val, 2));
// aggiunta di una funzione
$this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6);
}
Nel template:
<p>Prezzo: {$price|money}</p>
{if isWeekend($now)} ... {/if}
Per una logica più complessa potete configurare direttamente l'oggetto Latte\Engine:
protected function beforeRender(): void
{
$latte = $this->template->getLatte();
$latte->setFeature(Latte\Feature::MigrationWarnings);
}
Con gli attributi
Un approccio più elegante è definire filtri e funzioni come metodi direttamente nella classe dei parametri del template del presenter o del componente, contrassegnandoli con degli attributi:
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
#[Latte\Attributes\TemplateFilter]
public function money(float $val): string
{
return '$' . number_format($val, 2);
}
#[Latte\Attributes\TemplateFunction]
public function isWeekend(DateTimeInterface $date): bool
{
return $date->format('N') >= 6;
}
}
Latte individua e registra automaticamente i metodi contrassegnati con questi attributi. Il nome del filtro o della funzione nei template coincide con il nome del metodo. Questi metodi devono essere pubblici.
Globalmente, con le estensioni
Gli approcci precedenti si prestano a filtri e funzioni necessari solo in determinati presenter o componenti, non nell'intera applicazione. Per tutta l'applicazione la soluzione migliore è creare un'estensione. Questa classe centralizza tutte le estensioni di Latte del vostro progetto. Un breve esempio:
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
{
// ...
}
// ...
}
Registrate l'estensione tramite la configurazione:
latte:
extensions:
- App\Presentation\Accessory\LatteExtension
Le estensioni offrono diversi vantaggi: il supporto della dependency injection, l'accesso allo strato del modello della vostra applicazione e la gestione centralizzata di tutte le estensioni. Supportano inoltre tag personalizzati, provider, compiler pass e altro ancora.
Configurare tutti i template
Il servizio TemplateFactory, che crea tutti i template, offre un array pubblico di callback
$onCreate. Vengono chiamate ogni volta che viene creato un template qualsiasi, così potete impostare filtri,
funzioni o variabili per tutti i template dell'applicazione da un unico punto. Ogni callback riceve il template appena creato.
Fatevi iniettare il servizio
TemplateFactory e registrate le callback, per esempio all'avvio dell'applicazione:
$templateFactory->onCreate[] = function (Nette\Bridges\ApplicationLatte\Template $template): void {
$template->addFilter('money', fn($val) => '$' . number_format($val, 2));
};
Traduzione
Se programmate un'applicazione multilingue, probabilmente avrete bisogno di stampare nel template alcuni testi in lingue
diverse. Nette Framework definisce a questo scopo l'interfaccia di traduzione Nette\Localization\Translator, che ha un unico
metodo translate(). Accetta il messaggio $message, di norma una stringa, e qualsiasi altro parametro. Il
compito è restituire la stringa tradotta. Nette non ha un'implementazione predefinita; potete scegliere tra diverse soluzioni
già pronte disponibili su Componette, secondo le vostre esigenze. La
loro documentazione spiega come configurare il traduttore.
Ai template si può impostare un traduttore, che ci facciamo passare, con il metodo
setTranslator():
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
In alternativa il traduttore si può impostare tramite la configurazione:
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
Il traduttore si può poi usare, per esempio, come filtro |translate, compresi i parametri aggiuntivi che vengono
passati al metodo translate() (vedi foo, bar):
<a href="basket">{='Carrello'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
Oppure come tag con il trattino basso:
<a href="basket">{_'Carrello'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Per tradurre una porzione di template esiste il tag di tipo pari {translate} (da Latte 2.11, prima si usava il tag
{_}):
<a href="order">{translate}Ordine{/translate}</a>
<a href="order">{translate foo, bar}Ordine{/translate}</a>
Il traduttore viene normalmente chiamato in fase di esecuzione, durante il rendering del template. Latte versione 3 è però in grado di tradurre tutti i testi statici già durante la compilazione del template. Questo fa risparmiare prestazioni, perché ogni stringa viene tradotta una sola volta e la traduzione risultante viene scritta nella forma compilata. Nella directory della cache nascono così più versioni compilate del template, una per ogni lingua. Per ottenerlo basta indicare la lingua come secondo parametro:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator, $lang);
}
Per testo statico si intende, per esempio, {_'hello'} oppure {translate}hello{/translate}. I testi
non statici, come {_$foo}, continueranno a essere tradotti in fase di esecuzione.