Templates

O Nette usa o sistema de templates Latte. Por um lado, porque é o sistema de templates mais seguro para PHP e, ao mesmo tempo, o sistema mais intuitivo. Você não precisa aprender muito de novo, basta o conhecimento de PHP e algumas tags.

É comum que uma página seja composta por um template de layout + o template da ação específica. Assim pode parecer um template de layout, observe os blocos {block} e a tag {include}:

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

E este será o template da ação:

{block title}Página Inicial{/block}

{block content}
<h1>Página Inicial</h1>
...
{/block}

Ele define o bloco content, que será inserido no lugar de {include content} no layout, e também re-define o bloco title, que sobrescreverá {block title} no layout. Tente imaginar o resultado.

Procurando templates

Você não precisa especificar nos presenters qual template deve ser renderizado, o framework deduzirá o caminho por si só e economizará sua digitação.

Se você usa uma estrutura de diretórios onde cada presenter tem seu próprio diretório, simplesmente coloque o template neste diretório com o nome da ação (ou view), ou seja, para a ação default, use o template default.latte:

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

Se você usa uma estrutura onde os presenters estão juntos em um diretório e os templates na pasta templates, salve-o no arquivo <Presenter>.<view>.latte ou <Presenter>/<view>.latte:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1ª variante
        └── Home/
            └── default.latte   ← 2ª variante

O diretório templates também pode estar um nível acima, ou seja, no mesmo nível do diretório com as classes dos presenters.

Se o template não for encontrado, o presenter responderá com um erro 404 – página não encontrada.

A view é alterada usando $this->setView('outraView'). Também é possível especificar diretamente o arquivo de template usando $this->template->setFile('/caminho/para/template.latte').

Os arquivos onde os templates são procurados podem ser alterados sobrescrevendo o método formatTemplateFiles(), que retorna um array de possíveis nomes de arquivos.

Procurando o template de layout

O Nette também procura automaticamente o arquivo de layout.

Se você usa uma estrutura de diretórios onde cada presenter tem seu próprio diretório, coloque o layout ou na pasta com o presenter, se for específico apenas para ele, ou um nível acima, se for comum a vários presenters:

app/
└── Presentation/
    ├── @layout.latte           ← layout comum
    └── Home/
        ├── @layout.latte       ← apenas para o presenter Home
        ├── HomePresenter.php
        └── default.latte

Se você usa uma estrutura onde os presenters estão juntos em um diretório e os templates na pasta templates, o layout será esperado nestes locais:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← layout comum
        ├── Home.@layout.latte  ← apenas para Home, 1ª variante
        └── Home/
            └── @layout.latte   ← apenas para Home, 2ª variante

Se o presenter estiver em um módulo, a busca também ocorrerá em níveis de diretório superiores, de acordo com o aninhamento do módulo.

O nome do layout pode ser alterado usando $this->setLayout('layoutAdmin') e então será esperado no arquivo @layoutAdmin.latte. Também é possível especificar diretamente o arquivo de template de layout usando $this->setLayout('/caminho/para/template.latte').

Usando $this->setLayout(false) ou a tag {layout none} dentro do template, a busca por layout é desativada.

Os arquivos onde os templates de layout são procurados podem ser alterados sobrescrevendo o método formatLayoutTemplateFiles(), que retorna um array de possíveis nomes de arquivos.

Variáveis no template

Passamos variáveis para o template escrevendo-as em $this->template e depois as temos disponíveis no template como variáveis locais:

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

Desta forma simples, podemos passar quaisquer variáveis para os templates. No entanto, no desenvolvimento de aplicações robustas, geralmente é mais útil limitar-se. Por exemplo, definindo explicitamente a lista de variáveis que o template espera e seus tipos. Graças a isso, o PHP poderá verificar os tipos, o IDE sugerirá corretamente e a análise estática revelará erros.

E como definimos tal lista? Simplesmente na forma de uma classe e suas propriedades. Nomeamo-la de forma semelhante ao presenter, apenas com Template no final:

/**
 * @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 outras variáveis
}

O objeto $this->template no presenter será agora uma instância da classe ArticleTemplate. Assim, o PHP verificará os tipos declarados ao escrever. E a partir da versão PHP 8.2, também alertará sobre a escrita em uma variável inexistente; em versões anteriores, o mesmo pode ser alcançado usando a trait Nette\SmartObject.

A anotação @property-read destina-se ao IDE e à análise estática, graças a ela o autocompletar funcionará, veja PhpStorm and code completion for $this⁠-⁠>⁠template.

Você pode desfrutar do luxo do autocompletar também nos templates, basta instalar o plugin para Latte no PhpStorm e indicar o nome da classe no início do template, mais no artigo Latte: como usar o sistema de tipos:

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

É assim que os templates em componentes também funcionam, basta seguir a convenção de nomenclatura e para um componente, por exemplo, FifteenControl, criar uma classe de template FifteenTemplate.

Se precisar criar $template como uma instância de outra classe, use o método createTemplate():

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

Variáveis padrão

Presenters e componentes passam automaticamente várias variáveis úteis para os templates:

  • $basePath é o caminho URL absoluto para o diretório raiz (por exemplo, /loja)
  • $baseUrl é a URL absoluta para o diretório raiz (por exemplo, http://localhost/loja)
  • $user é o objeto representando o usuário
  • $presenter é o presenter atual
  • $control é o componente ou presenter atual
  • $flashes array de mensagens enviadas pela função flashMessage()

Se você usar sua própria classe de template, essas variáveis serão passadas se você criar uma propriedade para elas.

No template, links para outros presenters & ações são criados desta forma:

<a n:href="Product:show">detalhe do produto</a>

O atributo n:href é muito útil para tags HTML <a>. Se quisermos exibir o link em outro lugar, por exemplo, no texto, usamos {link}:

O endereço é: {link Home:default}

Mais informações podem ser encontradas no capítulo Criando Links URL.

Filtros personalizados, tags, etc.

O sistema de templates Latte pode ser estendido com filtros, funções, tags, etc. personalizados. Isso pode ser feito diretamente no método render<View> ou beforeRender():

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

	// ou configuramos diretamente o objeto Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

O Latte na versão 3 oferece uma maneira mais avançada, que é criar uma extensão para cada projeto web. Um exemplo fragmentado de tal classe:

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

	// ...
}

Nós a registramos usando a configuração:

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

Tradução

Se você está programando uma aplicação multilíngue, provavelmente precisará exibir alguns textos no template em diferentes idiomas. O Nette Framework define para este propósito uma interface para tradução Nette\Localization\Translator, que tem um único método translate(). Ele recebe a mensagem $message, que geralmente é uma string, e quaisquer outros parâmetros. A tarefa é retornar a string traduzida. No Nette, não há implementação padrão, você pode escolher de acordo com suas necessidades entre várias soluções prontas que podem ser encontradas na Componette. Em sua documentação, você aprenderá como configurar o tradutor.

É possível definir um tradutor para os templates, que solicitamos, usando o método setTranslator():

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

O tradutor também pode ser definido alternativamente através da configuração:

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

Depois, o tradutor pode ser usado, por exemplo, como um filtro |translate, incluindo parâmetros adicionais que são passados para o método translate() (veja foo, bar):

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

Ou como uma tag de sublinhado:

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

Para traduzir uma seção do template, existe uma tag de par {translate} (a partir do Latte 2.11, anteriormente usava-se a tag {_}):

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

O tradutor é chamado por padrão em tempo de execução durante a renderização do template. O Latte versão 3, no entanto, pode traduzir todos os textos estáticos já durante a compilação do template. Isso economiza desempenho, pois cada string é traduzida apenas uma vez e a tradução resultante é escrita na forma compilada. No diretório de cache, são criadas várias versões compiladas do template, uma para cada idioma. Para isso, basta apenas especificar o idioma como segundo parâmetro:

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

Texto estático significa, por exemplo, {_'olá'} ou {translate}olá{/translate}. Textos não estáticos, como {_$foo}, continuarão a ser traduzidos em tempo de execução.

versão: 4.0