Modelli

Nette utilizza il sistema di template Latte. Latte è utilizzato perché è il sistema di template più sicuro per PHP e allo stesso tempo il più intuitivo. Non è necessario imparare molto di nuovo, basta conoscere PHP e alcuni tag di Latte.

Di solito la pagina viene completata dal modello di layout + il modello di azione. Ecco come potrebbe apparire un modello di layout, notando 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 potrebbe essere il modello di 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 anche il blocco title, che sovrascrive {block title} nel layout. Provate a immaginare il risultato.

Ricerca di modelli

Nei presentatori, non è necessario specificare quale template debba essere reso; il framework determinerà automaticamente il percorso, semplificando la codifica.

Se si utilizza una struttura di cartelle in cui ogni presentatore ha una propria cartella, è sufficiente posizionare il template in questa cartella sotto il nome dell'azione (cioè della vista). Ad esempio, per l'azione default, utilizzare il modello default.latte:

app/
└── UI/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Se si utilizza una struttura in cui i presentatori sono riuniti in una directory e i modelli in una cartella templates, salvare il tutto in un file <Presenter>.<view>.latte oppure <Presenter>/<view>.latte:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1st variant
        └── Home/
            └── default.latte   ← 2nd variant

La directory templates può anche essere collocata un livello più in alto, allo stesso livello della directory con le classi dei presentatori.

Se il modello non viene trovato, il presentatore risponde con un errore 404 – pagina non trovata.

È possibile modificare la vista utilizzando $this->setView('anotherView'). È anche possibile specificare direttamente il file del modello con $this->template->setFile('/path/to/template.latte').

I file in cui vengono cercati i modelli possono essere modificati sovrascrivendo il metodo formatTemplateFiles(), che restituisce un array di possibili nomi di file.

Ricerca dei modelli di layout

Nette cerca automaticamente anche il file di layout.

Se si utilizza una struttura di directory in cui ogni presentatore ha una propria directory, collocare il layout nella cartella del presentatore, se è specifico solo per lui, oppure a un livello superiore se è comune a più presentatori:

app/
└── UI/
    ├── @layout.latte           ← common layout
    └── Home/
        ├── @layout.latte       ← only for Home presenter
        ├── HomePresenter.php
        └── default.latte

Se si utilizza una struttura in cui i presentatori sono raggruppati in una directory e i modelli si trovano in una cartella templates, il layout sarà previsto nei seguenti punti:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← common layout
        ├── Home.@layout.latte  ← only for Home, 1st variant
        └── Home/
            └── @layout.latte   ← only for Home, 2nd variant

Se il presentatore si trova in un modulo, cercherà anche più in alto nell'albero della directory, in base alla nidificazione del modulo.

Il nome del layout può essere modificato con $this->setLayout('layoutAdmin') e sarà previsto nel file @layoutAdmin.latte. È anche possibile specificare direttamente il file del modello di layout usando $this->setLayout('/path/to/template.latte').

L'uso di $this->setLayout(false) o del tag {layout none} all'interno del template disabilita la ricerca del layout.

I file in cui vengono cercati i modelli di layout possono essere modificati sovrascrivendo il metodo formatLayoutTemplateFiles(), che restituisce un array di possibili nomi di file.

Variabili nel modello

Le variabili vengono passate al template scrivendole in $this->template e poi sono disponibili nel template come variabili locali:

$this->template->article = $this->articles->getById($id);

In questo modo possiamo passare facilmente qualsiasi variabile ai template. Tuttavia, quando si sviluppano applicazioni robuste, spesso è più utile limitarsi. Per esempio, definendo esplicitamente un elenco di variabili che il template si aspetta e i loro tipi. Ciò consentirà a PHP di effettuare il controllo dei tipi, all'IDE di effettuare il completamento automatico in modo corretto e all'analisi statica di rilevare gli errori.

Come si definisce un'enumerazione di questo tipo? Semplicemente sotto forma di una classe e delle sue proprietà. La chiamiamo in modo simile a presenter, ma con Template alla fine:

/**
 * @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 presentatore sarà ora un'istanza della classe ArticleTemplate. Quindi PHP controllerà i tipi dichiarati quando vengono scritti. A partire da PHP 8.2, inoltre, avvertirà di scrivere su una variabile inesistente; nelle versioni precedenti si può ottenere lo stesso risultato utilizzando il tratto Nette\SmartObject.

L'annotazione @property-read è per l'IDE e l'analisi statica, farà funzionare il completamento automatico, vedere PhpStorm e il completamento del codice per $this->template.

Ci si può concedere il lusso di sussurrare anche nei template, basta installare il plugin Latte in PhpStorm e specificare il nome della classe all'inizio del template, si veda l'articolo Latte: come digitare il sistema:

{templateType App\UI\Article\ArticleTemplate}
...

Questo è anche il modo in cui i template funzionano nei componenti, basta seguire la convenzione di denominazione e creare una classe template FifteenTemplate per il componente, ad esempio FifteenControl.

Se si vuole creare una classe $template come istanza di un'altra classe, usare il metodo createTemplate():

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

Variabili predefinite

I presentatori e i componenti passano automaticamente diverse variabili utili ai modelli:

  • $basePath è un percorso URL assoluto alla cartella principale (ad esempio /CD-collection)
  • $baseUrl è un URL assoluto alla cartella principale (per esempio http://localhost/CD-collection)
  • $user è un oggetto che rappresenta l'utente
  • $presenter è il presentatore corrente
  • $control è il componente o presentatore corrente
  • $flashes è un elenco di messaggi inviati dal metodo flashMessage()

Se si utilizza una classe modello personalizzata, queste variabili vengono passate se si crea una proprietà per esse.

Nel modello si creano collegamenti ad altri presentatori e azioni come segue:

<a n:href="Product:show">detail</a>

L'attributo n:href è molto utile per i tag HTML <a>. Se vogliamo stampare il link altrove, ad esempio nel testo, usiamo {link}:

URL is: {link Home:default}

Per ulteriori informazioni, vedere Creazione di collegamenti.

Filtri personalizzati, tag, ecc.

Il sistema di template Latte può essere esteso con filtri personalizzati, funzioni, tag, ecc. Questo può essere fatto direttamente nel metodo render<View> o nel metodo beforeRender():

public function beforeRender(): void
{
	// aggiunta di un filtro
	$this->template->addFilter('foo', /* ... */);

	// oppure configurare direttamente l'oggetto LatteEngine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

La versione 3 di Latte offre un metodo più avanzato, creando un'estensione per ogni progetto web. Ecco un esempio approssimativo di una classe di questo tipo:

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()),
			// ...
		];
	}

	// ...
}

La registriamo usando configuration:

latte:
	extensions:
		- App\UI\Accessory\LatteExtension

Tradurre

Se si sta programmando un'applicazione multilingue, è probabile che sia necessario produrre parte del testo del modello in lingue diverse. A tale scopo, il framework Nette definisce un'interfaccia di traduzione Nette\Localization\Translator, che ha un unico metodo translate(). Questo accetta il messaggio $message, che di solito è una stringa, e qualsiasi altro parametro. Il compito è quello di restituire la stringa tradotta. Non esiste un'implementazione predefinita in Nette, ma si può scegliere in base alle proprie esigenze tra diverse soluzioni già pronte che si possono trovare su Componette. La loro documentazione spiega come configurare il traduttore.

I modelli possono essere configurati con un traduttore, che ci verrà passato, utilizzando il metodo setTranslator():

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

In alternativa, il traduttore può essere impostato utilizzando la configurazione:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Il traduttore può essere utilizzato, ad esempio, come filtro |translate, con parametri aggiuntivi passati al metodo translate() (vedere foo, bar):

<a href="basket">{='Basket'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

Oppure come tag underscore:

<a href="basket">{_'Basket'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

Per la traduzione di sezioni di template, esiste un tag accoppiato {translate} (da Latte 2.11, in precedenza si usava il tag {_} ):

<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>

Il traduttore viene chiamato per impostazione predefinita in fase di esecuzione durante il rendering del template. La versione 3 di Latte, tuttavia, può tradurre tutto il testo statico durante la compilazione del modello. Ciò consente di risparmiare sulle prestazioni, perché ogni stringa viene tradotta una sola volta e la traduzione risultante viene scritta nel modello compilato. In questo modo si creano più versioni compilate del modello nella cartella cache, una per ogni lingua. Per farlo, è sufficiente specificare la lingua come secondo parametro:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

Per testo statico si intende, ad esempio, {_'hello'} o {translate}hello{/translate}. Il testo non statico, come {_$foo}, continuerà a essere compilato al volo.