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 dei modelli

Il percorso dei modelli viene dedotto secondo una semplice logica. Si cerca di vedere se uno di questi file di template esiste relativamente alla directory in cui si trova la classe del presentatore, dove <Presenter> è il nome del presentatore corrente e <view> è il nome dell'azione corrente:

  • templates/<Presenter>/<view>.latte
  • templates/<Presenter>.<view>.latte

Se il modello non viene trovato, si cercherà nella cartella templates a un livello superiore, cioè allo stesso livello della cartella con la classe del presentatore.

Se il modello non viene trovato nemmeno lì, la risposta è un errore 404.

Si può anche cambiare la vista usando $this->setView('otherView'). Oppure, invece di cercare, specificare direttamente il nome del file del template usando $this->template->setFile('/path/to/template.latte').

È possibile modificare i percorsi in cui vengono cercati i modelli sovrascrivendo il metodo formatTemplateFiles, che restituisce un array di possibili percorsi di file.

Il layout è previsto nei seguenti file:

  • templates/<Presenter>/@<layout>.latte
  • templates/<Presenter>.@<layout>.latte
  • templates/@<layout>.latte layout comune a più presentatori

<Presenter> è il nome del presentatore corrente e <layout> è il nome del layout, che per impostazione predefinita è 'layout'. Il nome può essere modificato con $this->setLayout('otherLayout'), in modo da provare i file @otherLayout.latte.

È anche possibile specificare direttamente il nome del file del modello di layout con $this->setLayout('/path/to/template.latte'). L'uso di $this->setLayout(false) disabilita la ricerca dei layout.

È possibile modificare i percorsi in cui vengono cercati i modelli sovrascrivendo il metodo formatLayoutTemplateFiles, che restituisce un array di possibili percorsi 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\Presenters\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\Templating;

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\Templating\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

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.