Modelos
A Nette utiliza o sistema de modelos Latte. O Latte é usado porque é o sistema de modelo mais seguro para PHP e, ao mesmo tempo, o sistema mais intuitivo. Você não precisa aprender muito de novo, você só precisa conhecer PHP e algumas tags de Latte.
É normal que a página seja completada a partir do modelo de layout + o modelo de ação. Isto é o que um modelo de layout
pode parecer, observe os blocos {block}
e a etiqueta {include}
:
<!DOCTYPE html>
<html>
<head>
<title>{block title}My App{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
E este poderia ser o modelo de ação:
{block title}Homepage{/block}
{block content}
<h1>Homepage</h1>
...
{/block}
Ele define o bloco content
, que é inserido no lugar de {include content}
no layout, e também
redefine o bloco title
, que sobrescreve {block title}
no layout. Tente imaginar o resultado.
Pesquisa de modelos
Nos apresentadores, você não precisa especificar qual modelo deve ser renderizado; a estrutura determinará automaticamente o caminho, facilitando a codificação para você.
Se você usar uma estrutura de diretórios em que cada apresentador tenha seu próprio diretório, basta colocar o modelo
nesse diretório com o nome da ação (ou seja, visualização). Por exemplo, para a ação default
, use o modelo
default.latte
:
app/ └── UI/ └── Home/ ├── HomePresenter.php └── default.latte
Se você usar uma estrutura em que os apresentadores estejam juntos em um diretório e os modelos em uma pasta
templates
, salve-os em um arquivo <Presenter>.<view>.latte
ou
<Presenter>/<view>.latte
:
app/ └── Presenters/ ├── HomePresenter.php └── templates/ ├── Home.default.latte ← 1st variant └── Home/ └── default.latte ← 2nd variant
O diretório templates
também pode ser colocado um nível acima, no mesmo nível do diretório com as classes de
apresentador.
Se o modelo não for encontrado, o apresentador responderá com o erro 404 – página não encontrada.
Você pode alterar a visualização usando $this->setView('anotherView')
. Também é possível especificar
diretamente o arquivo de modelo com $this->template->setFile('/path/to/template.latte')
.
Os arquivos onde os modelos são pesquisados podem ser alterados substituindo o método formatTemplateFiles(), que retorna uma matriz de possíveis nomes de arquivos.
Pesquisa de modelos de layout
O Nette também procura automaticamente o arquivo de layout.
Se você usar uma estrutura de diretórios em que cada apresentador tenha seu próprio diretório, coloque o layout na pasta com o apresentador, se for específico apenas para ele, ou em um nível superior, se for comum a vários apresentadores:
app/ └── UI/ ├── @layout.latte ← common layout └── Home/ ├── @layout.latte ← only for Home presenter ├── HomePresenter.php └── default.latte
Se você usar uma estrutura em que os apresentadores estejam agrupados em um diretório e os modelos estejam em uma pasta
templates
, o layout será esperado nos seguintes locais:
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 o apresentador estiver em um módulo, ele também pesquisará mais acima na árvore de diretórios, de acordo com o aninhamento do módulo.
O nome do layout pode ser alterado usando $this->setLayout('layoutAdmin')
e, em seguida, ele será esperado no
arquivo @layoutAdmin.latte
. Você também pode especificar diretamente o arquivo de modelo de layout usando
$this->setLayout('/path/to/template.latte')
.
O uso de $this->setLayout(false)
ou da tag {layout none}
dentro do modelo desativa a pesquisa de
layout.
Os arquivos em que os modelos de layout são pesquisados podem ser alterados substituindo o método formatLayoutTemplateFiles(), que retorna uma matriz de possíveis nomes de arquivos.
Variáveis no modelo
As variáveis são passadas para o modelo, escrevendo-as para $this->template
e depois estão disponíveis no
modelo como variáveis locais:
$this->template->article = $this->articles->getById($id);
Desta forma, podemos facilmente passar qualquer variável para os modelos. Entretanto, ao desenvolver aplicações robustas, muitas vezes é mais útil nos limitarmos. Por exemplo, ao definir explicitamente uma lista de variáveis que o modelo espera e seus tipos. Isto permitirá que o PHP verifique a digitação, que a IDE se autocomplete corretamente, e que a análise estática detecte erros.
E como definimos tal enumeração? Simplesmente sob a forma de uma classe e suas propriedades. Damos o mesmo nome ao
apresentador, mas 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
do apresentador será agora uma instância da classe ArticleTemplate
.
Portanto, o PHP verificará os tipos declarados quando eles forem escritos. E a partir do PHP 8.2 ele também alertará sobre a
escrita de uma variável inexistente, nas versões anteriores o mesmo pode ser alcançado usando o traço Nette\SmartObject.
A anotação @property-read
é para IDE e análise estática, fará com que o auto-completar funcione, veja PhpStorm e preenchimento de código por
$this->template.
Você também pode se dar ao luxo de sussurrar nos modelos, basta instalar o plugin Latte no PhpStorm e especificar o nome da classe no início do modelo, veja o artigo Latte: como digitar sistema:
{templateType App\UI\Article\ArticleTemplate}
...
Também é assim que os modelos funcionam nos componentes, basta seguir a convenção de nomenclatura e criar uma classe de
modelos FifteenTemplate
para o componente, por exemplo FifteenControl
.
Se você precisar criar um $template
como 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
Apresentadores e componentes passam várias variáveis úteis para os modelos automaticamente:
$basePath
é um caminho absoluto de URL para o dir raiz (por exemplo/CD-collection
)$baseUrl
é um URL absoluto para o dir raiz (por exemplohttp://localhost/CD-collection
)$user
é um objeto que representa o usuário$presenter
é o atual apresentador$control
é o atual componente ou apresentador$flashes
lista de mensagens enviadas por métodoflashMessage()
Se você usar uma classe de modelo personalizada, estas variáveis são passadas se você criar uma propriedade para elas.
Criação de links
No modelo, criamos links para outros apresentadores e ações da seguinte forma:
<a n:href="Product:show">detail</a>
O atributo n:href
é muito útil para tags HTML <a>
. Se quisermos imprimir o link em outro
lugar, por exemplo, no texto, usamos {link}
:
URL is: {link Home:default}
Para mais informações, consulte Criação de links.
Filtros personalizados, Etiquetas, etc.
O sistema de modelos Latte pode ser ampliado com filtros personalizados, funções, tags, etc. Isto pode ser feito diretamente
no render<View>
ou beforeRender()
método:
public function beforeRender(): void
{
// adicionando um filtro
$this->template->addFilter('foo', /* ... */);
// ou configurar diretamente o objeto Latte\Engine
$latte = $this->template->getLatte();
$latte->addFilterLoader(/* ... */);
}
A versão 3 do Latte oferece uma maneira mais avançada, criando uma extensão para cada projeto web. Aqui está um exemplo rudimentar de tal classe:
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()),
// ...
];
}
// ...
}
Registramos usando a configuração#Latte:
latte:
extensions:
- App\UI\Accessory\LatteExtension
Traduzindo
Se você estiver programando uma aplicação multilíngüe, provavelmente precisará produzir parte do texto no modelo em
diferentes idiomas. Para fazer isto, o Nette Framework define uma interface de tradução Nette\Localization\Translator, que tem um único
método translate()
. Isto aceita a mensagem $message
, que geralmente é uma string, e quaisquer outros
parâmetros. A tarefa é devolver a string traduzida. Não há uma implementação padrão em Nette, você pode escolher de
acordo com suas necessidades entre várias soluções prontas que podem ser encontradas na Componette. Sua documentação lhe diz como configurar o tradutor.
Os modelos podem ser criados com um tradutor, que teremos passado para nós, usando o método
setTranslator()
:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
Alternativamente, o tradutor pode ser definido usando a configuração:
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
O tradutor pode então ser usado, por exemplo, como um filtro |translate
, com parâmetros adicionais passados
para o método translate()
(ver foo, bar
):
<a href="basket">{='Basket'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
Ou como uma etiqueta de sublinhado:
<a href="basket">{_'Basket'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Para a tradução da seção de modelos, há uma etiqueta emparelhada {translate}
(desde Latte 2.11, anteriormente
foi usada a etiqueta {_}
):
<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>
O tradutor é chamado por padrão em tempo de execução ao renderizar o modelo. Latte versão 3, no entanto, pode traduzir todo o texto estático durante a compilação do modelo. Isto economiza desempenho porque cada string é traduzida apenas uma vez e a tradução resultante é escrita na forma compilada. Isto cria múltiplas versões compiladas do modelo no diretório do cache, uma para cada idioma. Para fazer isto, basta especificar o idioma como segundo parâmetro:
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator, $lang);
}
Por texto estático entendemos, por exemplo, {_'hello'}
ou {translate}hello{/translate}
. Textos não
estáticos, como {_$foo}
, continuarão a ser compilados em tempo real.