Templates

Nette uses the Latte template system. Latte is used because it is the most secure template system for PHP, and at the same time the most intuitive system. You don't have to learn much new, you just need to know PHP and a few Latte tags.

It is usual that the page is completed from the layout template + the action template. This is what a layout template might look like, notice the blocks {block} and tag {include}:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

And this might be the action template:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

It defines block content, which is inserted in place of {include content} in the layout, and also re-defines block title, which overwrites {block title} in the layout. Try to imagine the result.

Template Lookup

In presenters, you don't need to specify which template should be rendered; the framework will automatically determine the path, making coding easier for you.

If you use a directory structure where each presenter has its own directory, simply place the template in this directory under the name of the action (i.e. view). For example, for the default action, use the default.latte template:

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

If you use a structure where presenters are together in one directory and templates in a templates folder, save it either in a file <Presenter>.<view>.latte or <Presenter>/<view>.latte:

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

The templates directory can also be placed one level higher, at the same level as the directory with presenter classes.

If the template is not found, the presenter responds with 404 – page not found error.

You can change the view using $this->setView('anotherView'). It is also possible to directly specify the template file with $this->template->setFile('/path/to/template.latte').

Files where templates are searched can be changed by overriding the method formatTemplateFiles(), which returns an array of possible file names.

Layout Template Lookup

Nette also automatically searches for the layout file.

If you use a directory structure where each presenter has its own directory, place the layout either in the folder with the presenter, if it is specific only to them, or a level higher if it is common to multiple presenters:

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

If you use a structure where presenters are grouped together in one directory and templates are in a templates folder, the layout will be expected in the following places:

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

If the presenter is in a module, it will also search further up the directory tree according to the module's nesting.

The name of the layout can be changed using $this->setLayout('layoutAdmin') and then it will be expected in the file @layoutAdmin.latte. You can also directly specify the layout template file using $this->setLayout('/path/to/template.latte').

Using $this->setLayout(false) or the {layout none} tag inside the template disables layout search.

Files where layout templates are searched can be changed by overriding the method formatLayoutTemplateFiles(), which returns an array of possible file names.

Variables in the Template

Variables are passed to the template by writing them to $this->template and then they are available in the template as local variables:

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

In this way we can easily pass any variables to the templates. However, when developing robust applications, it is often more useful to limit ourselves. For example, by explicitly defining a list of variables that the template expects and their types. This will allow PHP to type-check, the IDE to autocomplete correctly, and static analysis to detect errors.

And how do we define such an enumeration? Simply in the form of a class and its properties. We name it similarly to presenter, but with Template at the end:

/**
 * @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;

	// and other variables
}

The $this->template object in the presenter will now be an instance of the ArticleTemplate class. So PHP will check the declared types when they are written. And starting with PHP 8.2 it will also warn about writing to a non-existent variable, in previous versions the same can be achieved using the Nette\SmartObject trait.

The @property-read annotation is for IDE and static analysis, it will make autocomplete work, see PhpStorm and code completion for $this->template.

You can indulge in the luxury of whispering in templates too, just install the Latte plugin in PhpStorm and specify the class name at the beginning of the template, see the article Latte: how to type system:

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

This is also how templates work in components, just follow the naming convention and create a template class FifteenTemplate for the component e.g. FifteenControl.

If you need to create a $template as an instance of another class, use the createTemplate() method:

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

Default Variables

Presenters and components pass several useful variables to templates automatically:

  • $basePath is an absolute URL path to root dir (for example /CD-collection)
  • $baseUrl is an absolute URL to root dir (for example http://localhost/CD-collection)
  • $user is an object representing the user
  • $presenter is the current presenter
  • $control is the current component or presenter
  • $flashes list of messages sent by method flashMessage()

If you use a custom template class, these variables are passed if you create a property for them.

In template we create links to other presenters & actions as follows:

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

Attribute n:href is very handy for HTML tags <a>. If we want to print the link elsewhere, for example in the text, we use {link}:

URL is: {link Home:default}

For more information, see Creating Links.

Custom Filters, Tags, etc.

The Latte templating system can be extended with custom filters, functions, tags, etc. This can be done directly in the render<View> or beforeRender() method:

public function beforeRender(): void
{
	// adding a filter
	$this->template->addFilter('foo', /* ... */);

	// or configure the Latte\Engine object directly
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latte version 3 offers a more advanced way by creating an extension for each web project. Here is a rough example of such a class:

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

	// ...
}

We register it using configuration:

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

Translating

If you are programming a multilingual application, you will probably need to output some of the text in the template in different languages. To do this, the Nette Framework defines a translation interface Nette\Localization\Translator, which has a single method translate(). This accepts the message $message, which is usually a string, and any other parameters. The task is to return the translated string. There is no default implementation in Nette, you can choose according to your needs from several ready-made solutions that can be found on Componette. Their documentation tells you how to configure the translator.

Templates can be set up with a translator, which we will have passed to us, using the setTranslator() method:

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

Alternatively, the translator can be set using the configuration:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension

The translator can then be used, for example, as a filter |translate, with additional parameters passed to the translate() method (see foo, bar):

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

Or as an underscore tag:

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

For template section translation, there is a paired tag {translate} (since Latte 2.11, previously the tag {_} was used ):

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

Translator is called by default at runtime when rendering the template. Latte version 3, however, can translate all static text during template compilation. This saves performance because each string is translated only once and the resulting translation is written to the compiled form. This creates multiple compiled versions of the template in the cache directory, one for each language. To do this, you only need to specify the language as the second parameter:

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

By static text we mean, for example, {_'hello'} or {translate}hello{/translate}. Non-static text, such as {_$foo}, will continue to be compiled on the fly.