Herança e Reutilização de Modelos
Os mecanismos de reutilização e herança dos modelos estão aqui para aumentar sua produtividade porque cada modelo contém apenas seu conteúdo único e os elementos e estruturas repetidos são reutilizados. Introduzimos três conceitos: herança de layout, reutilização horizontal e herança de unidade.
O conceito de herança de modelos Latte é semelhante ao de herança de classe PHP. Você define um **modelo pai*** que outros **modelos filhos*** podem estender e podem sobrepor-se a partes do modelo pai. Funciona muito bem quando os elementos compartilham uma estrutura comum. Parece complicado? Não se preocupe, não é.
Herança do Layout {layout}
Vamos analisar a herança do modelo de layout começando com um exemplo. Este é um template pai que chamaremos por exemplo
layout.latte
e ele define um documento de esqueleto HTML.
As etiquetas {block}
definem três blocos que os modelos infantis podem preencher. Tudo o que a tag do bloco faz
é dizer ao motor do modelo que um modelo infantil pode substituir essas partes do modelo, definindo seu próprio bloco com
o mesmo nome.
Um modelo infantil pode ser parecido com este:
A tag {layout}
é a chave aqui. Ela diz ao motor do modelo que este modelo „estende“ outro modelo. Quando
Latte renderiza este modelo, primeiro ele localiza o modelo pai – neste caso, layout.latte
.
Nesse momento, o motor do modelo notará as três etiquetas de blocos em layout.latte
e substituirá esses blocos
pelo conteúdo do modelo infantil. Note que, como o modelo criança não definiu o bloco página, o conteúdo do modelo
pai é usado em seu lugar. O conteúdo dentro de uma tag {block}
em um modelo pai é sempre usado como um
recurso.
O resultado pode ser parecido:
Em um modelo infantil, os blocos só podem ser localizados no nível superior ou dentro de outro bloco, ou seja
Também será sempre criado um bloco, independentemente de a condição ao redor {if}
ser avaliada como verdadeira
ou falsa. Ao contrário do que você possa pensar, este modelo define um bloco.
Se você quiser que a saída dentro do bloco seja exibida condicionalmente, use ao invés disso o seguinte
Os dados fora de um bloco em um modelo infantil são executados antes que o modelo de layout seja apresentado, assim você
pode usá-lo para definir variáveis como {var $foo = bar}
e propagar dados para toda a cadeia de herança:
Herança Multilevel
Você pode usar tantos níveis de herança quantos forem necessários. Uma maneira comum de usar a herança de layout é a seguinte abordagem em três níveis:
- Crie um modelo
layout.latte
que contenha a aparência principal de seu site. - Crie um modelo
layout-SECTIONNAME.latte
para cada seção de seu site. Por exemplo,layout-news.latte
,layout-blog.latte
etc. Todos estes modelos estendemlayout.latte
e incluem estilos/design específicos de seção. - Crie modelos individuais para cada tipo de página, tais como um artigo de notícia ou entrada no blog. Estes modelos estendem o modelo apropriado da seção.
Hereditariedade de layout dinâmico
Você pode usar uma variável ou qualquer expressão PHP como o nome do modelo pai, assim a herança pode se comportar dinamicamente:
Você também pode usar o Latte API para escolher o modelo de layout automaticamente.
Dicas
Aqui estão algumas dicas para trabalhar com a herança de layout:
- Se você usar
{layout}
em um template, ele deve ser a primeira etiqueta do template nesse template. - O layout pode ser pesquisado automaticamente (como nos apresentadores). Nesse caso, se o modelo não tiver um
layout, ele indicará isso com a tag
{layout none}
. - A tag
{layout}
tem o pseudônimo{extends}
. - O nome do arquivo do modelo estendido depende do carregador de modelos.
- Você pode ter tantos blocos quantos quiser. Lembre-se, os modelos infantis não precisam definir todos os blocos dos pais, assim você pode preencher padrões razoáveis em um número de blocos, e depois definir apenas os que você precisa mais tarde.
Blocos {block}
Veja também anônimo {block}
Um bloco fornece uma maneira de mudar a forma como uma determinada parte de um modelo é renderizada, mas não interfere de forma alguma com a lógica ao seu redor. Tomemos o seguinte exemplo para ilustrar como um bloco funciona e, mais importante, como ele não funciona:
Se você renderizar este modelo, o resultado seria exatamente o mesmo com ou sem as etiquetas de bloco. Os blocos têm acesso a variáveis de escopos externos. É apenas uma forma de torná-lo anulável por um modelo infantil:
Agora, ao renderizar o modelo criança, o laço vai usar o bloco definido no modelo criança child.Latte
ao
invés do definido no modelo base parent.Latte
; o modelo executado é então equivalente ao seguinte:
Entretanto, se criarmos uma nova variável dentro de um bloco nomeado ou substituirmos um valor de um já existente, a mudança será visível apenas dentro do bloco:
O conteúdo do bloco pode ser modificado por filtros. O exemplo a seguir remove todo HTML e o título do bloco:
A etiqueta também pode ser escrita como n:atributo:
Blocos locais
Cada bloco substitui o conteúdo do bloco pai com o mesmo nome. Exceto para blocos locais. Eles são algo como métodos privados na classe. Você pode criar um modelo sem se preocupar que – devido à coincidência de nomes de blocos – eles seriam sobregravados por um segundo modelo.
Blocos de Impressão {include}
Veja também {include file}
Para imprimir um bloco em um lugar específico, use a tag {include blockname}
:
Você também pode exibir bloco a partir de outro modelo:
O bloco impresso não tem acesso às variáveis do contexto ativo, exceto se o bloco estiver definido no mesmo arquivo onde está incluído. No entanto, eles têm acesso às variáveis globais.
Você pode passar variáveis para o bloco da seguinte maneira:
Você pode usar uma variável ou qualquer expressão em PHP como o nome do bloco. Neste caso, adicione a palavra-chave
block
antes da variável, para que se saiba em tempo de compilação que se trata de um bloco, e não insira um modelo, cujo nome também poderia estar na variável:
O bloco também pode ser impresso dentro dele mesmo, o que é útil, por exemplo, ao renderizar uma estrutura em árvore:
Em vez de {include menu, ...}
, também podemos escrever {include this, ...}
onde this
significa bloco atual.
O conteúdo impresso pode ser modificado por filtros. O exemplo a seguir remove todo o HTML e o título do arquivo:
Bloco dos Pais
Se você precisar imprimir o conteúdo do bloco a partir do modelo pai, a declaração {include parent}
fará
o truque. Isto é útil se você quiser adicionar ao conteúdo de um bloco pai, em vez de substituí-lo completamente.
Definições {define}
Além dos blocos, há também „definições“ em Latte. Elas são comparáveis com funções em linguagens de programação regulares. Elas são úteis para reutilizar fragmentos de modelos para não se repetir.
O Latte tenta manter as coisas simples, portanto, basicamente, as definições são iguais aos blocos e tudo o que é dito sobre blocos também se aplica às definições. Elas diferem dos blocos no seguinte aspecto:
- são colocadas em tags
{define}
- são renderizadas somente quando são inseridas via
{include}
- você pode definir parâmetros para elas como funções no PHP
Imagine que você tenha um modelo auxiliar com uma coleção de definições sobre como desenhar formulários HTML.
Os argumentos de uma definição são sempre opcionais com valor padrão null
, a menos que o valor padrão seja
especificado (aqui 'text'
é o valor padrão para $type
). Os tipos de parâmetros também podem ser
declarados: {define input, string $name, ...}
.
O modelo com as definições é carregado usando {import}
. As próprias
definições são renderizadas da mesma forma que os blocos:
As definições não têm acesso às variáveis do contexto ativo, mas têm acesso às variáveis globais.
Nomes de blocos dinâmicos
O Latte permite grande flexibilidade na definição de blocos porque o nome do bloco pode ser qualquer expressão PHP. Este
exemplo define três blocos chamados hi-Peter
, hi-John
e hi-Mary
:
Por exemplo, podemos redefinir apenas um bloco em um modelo infantil:
Assim, a produção será parecida com esta:
Verificação da existência do bloco {ifset}
Veja também {ifset $var}
Use o teste {ifset blockname}
para verificar se um bloco (ou mais blocos) existe no contexto atual:
Você pode usar uma variável ou qualquer expressão em PHP como o nome do bloco. Neste caso, adicione a palavra-chave
block
antes da variável para deixar claro que não é a variável que é
verificada:
A existência de blocos também é retornada pela função hasBlock()
:
Dicas
Aqui estão algumas dicas para trabalhar com blocos:
- O último bloco de nível superior não precisa ter etiqueta de fechamento (o bloco termina com o final do documento). Isto simplifica a escrita de modelos infantis, que é um bloco primário.
- Para uma legibilidade extra, você pode opcionalmente dar um nome à sua tag
{/block}
, por exemplo{/block footer}
. Entretanto, o nome deve corresponder ao nome do bloco. Em modelos maiores, esta técnica ajuda você a ver quais etiquetas de bloco estão sendo fechadas. - Você não pode definir diretamente várias etiquetas de bloco com o mesmo nome no mesmo modelo. Mas isto pode ser conseguido usando nomes de blocos dinâmicos.
- Você pode usar n:atributos para definir blocos
como
<h1 n:block=title>Welcome to my awesome homepage</h1>
- Os blocos também podem ser usados sem nomes apenas para aplicar os filtros à
saída:
{block|strip} hello {/block}
Reutilização Horizontal {import}
A reutilização horizontal é o terceiro mecanismo de reutilização e herança no Latte. Ele permite carregar blocos de
outros modelos. É semelhante a criar um arquivo com funções auxiliares em PHP e depois carregá-lo usando
require
.
Embora a herança de layout de modelo seja um dos recursos mais avançados do Latte, ela é limitada à herança simples – um modelo só pode estender um outro modelo. A reutilização horizontal é uma forma de obter herança múltipla.
Vamos ter um conjunto de definições de blocos:
Usando o comando {import}
, importe todos os blocos e definições definidos em
blocks.latte
para outro modelo:
Se você importar os blocos do modelo pai (ou seja, usar {import}
em layout.latte
), os blocos também
estarão disponíveis em todos os modelos filhos, o que é muito útil.
O modelo que se pretende importar (por exemplo, blocks.latte
) não deve estender outro modelo, ou seja, usar {layout}
. Entretanto, ele pode importar
outros modelos.
A tag {import}
deve ser a primeira tag modelo após {layout}
. O nome do template pode ser qualquer
expressão PHP:
Você pode usar tantas declarações {import}
quantas quiser em qualquer modelo dado. Se dois gabaritos importados
definirem o mesmo bloco, o primeiro ganha. Entretanto, a maior prioridade é dada ao modelo principal, que pode sobrescrever
qualquer bloco importado.
O conteúdo dos blocos sobrescritos pode ser preservado inserindo-se o bloco da mesma forma que um bloco pai:
Neste exemplo, {include parent}
chamará corretamente o bloco sidebar
a partir do modelo
blocks.latte
.
Herança da Unidade {embed}
A herança de unidade leva a idéia de herança de layout ao nível de fragmentos de conteúdo. Enquanto o layout inheritance funciona com „esqueletos de documentos“, que são trazidos à vida por modelos de crianças, a herança de unidade permite criar esqueletos para unidades menores de conteúdo e reutilizá-los onde você quiser.
Na unidade de herança, a chave é a tag {embed}
. Ela combina o comportamento de {include}
e
{layout}
. Ela permite incluir outro modelo ou conteúdo de bloco e, opcionalmente, passar variáveis, assim como
{include}
faz. Ela também permite que você substitua qualquer bloco definido dentro do modelo incluído, como o
{layout}
faz.
Por exemplo, vamos utilizar o elemento de acordeão dobrável. Vamos dar uma olhada no esqueleto do elemento no modelo
collapsible.latte
:
As etiquetas {block}
definem dois blocos que os modelos infantis podem preencher. Sim, como no caso do modelo pai
no modelo de herança de layout. Você também pode ver a variável $modifierClass
.
Vamos usar nosso elemento no modelo. É aqui que entra {embed}
. É um kit super poderoso que nos permite fazer
todas as coisas: incluir o conteúdo do elemento no template, adicionar variáveis a ele e adicionar blocos com HTML
personalizado a ele:
O resultado pode ser parecido:
Blocos dentro de etiquetas embutidas formam uma camada separada independente de outros blocos. Portanto, eles podem ter
o mesmo nome que o bloco fora do encaixe e não são afetados de forma alguma. Usando a tag inclua dentro de {embed}
tags você pode inserir blocos aqui criados, blocos de
modelo embutido (que * não são* locais), e também blocos de modelo principal que são
locais. Você também pode importar blocos de outros arquivos:
Os modelos incorporados não têm acesso às variáveis do contexto ativo, mas eles têm acesso às variáveis globais.
Com {embed}
você pode inserir não apenas modelos, mas também outros blocos, de modo que o exemplo anterior
poderia ser escrito desta forma:
Se passarmos uma expressão para {embed}
e não estiver claro se se trata de um bloco ou nome de arquivo,
acrescente a palavra-chave block
ou file
:
Casos de uso
Há vários tipos de herança e reutilização de código em Latte. Vamos resumir os principais conceitos para uma maior liberação:
{include template}
Use Case: Usando header.latte
& footer.latte
dentro de layout.latte
.
header.latte
footer.latte
layout.latte
{layout}
**Uso caso***: Estendendo layout.latte
dentro de homepage.latte
& about.latte
.
layout.latte
homepage.latte
about.latte
{import}
**Use Case***: sidebar.latte
em single.product.latte
& single.service.latte
.
sidebar.latte
single.product.latte
single.service.latte
{define}
**Uso caso***: Uma função que obtém algumas variáveis e produz alguma marcação.
form.latte
profile.service.latte
{embed}
**Uso caso***: Embutir pagination.latte
em product.table.latte
&
service.table.latte
.
pagination.latte
product.table.latte
service.table.latte