Twig – Template Engine para PHP
Twig é um Engine de Templates para PHP que ajuda a separar a camada de visualização(view) da sua aplicação das outras, melhorando a separação do HTML do seu código PHP fornecendo um melhor reaproveitamento de código HTML. Ele é desenvolvida pela Symphony e pode ser integrado a diversos frameworks como, por exemplo, Slim e Laravel.
Para utilizar o Twig basta adicioná-lo como dependência do composer.
composer require "twig/twig:^3.0"
O objeto central do Twig é o Environment
(Twig\Environment
), nele serão guardadas as configurações e extensões, sendo utilizado para carregar os templates.
É bastante comum criar o objeto de Environment
na inicialização da aplicação e o utilizá-lo dentro dos controllers para carregar os templates.
A configuração básica do Environment
pode ser vista abaixo:
require('./vendor/autoload.php');
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
$loader = new FilesystemLoader('templates');
$twig = new Environment($loader, [
'cache' => false,
]);
echo $twig->render('index.html', ['nome' => 'Rodrigo']);
Começamos carregando o diretório onde os arquivos de template irão ser armazenados, utilizando para isso o objeto FilesystemLoader
. Neste exemplo o diretório de templates irá ficar no mesmo diretório do arquivo de exemplo.
Após o loader ser criado criamos o objeto de Environment
passando para seu construtor o loader criado e um array de opções. No momento passamos apenas a cache com o valor false para não utilizar. Veremos melhor as opções logo a seguir.
Com o objeto de Environment
podemos renderizar um arquivo de template para isso chamamos o método render
passando como parâmetro o arquivo que desejamos renderizar(que deve estar dentro da pasta templates) e um array associativo. O array associativo é onde passamos as variáveis para o template, sendo cada chave uma variável disponível contendo o valor associado.
O método render
irá retornar o conteúdo do template já processado pronto para ser exibido para o usuário, fazemos isso com o clássico echo
.
O objeto Environment
pode receber diversas opções:
cache
: Aceita uma string de caminho para o diretório de arquivos de cache. Ou false (padrão) para desabilitar. Para cada arquivo de template é criado um arquivo php no diretório estipulado.auto_reload
: Aceita um booleano indicando se o template será recompilado a cada alteração do arquivo. Recomendável durante o desenvolvimento.charset
: configura o charset usado no template. Padrão utf-8.strict_variables
: se false o Twig irá silenciar as variáveis inválidas/não existentes dentro do template. Se true irá lançar exceções. Padrão false.autoescape
: configura a estratégia de auto-escape de caracteres especiais. Aceitaname
,html
,js
,css
,url
,html_attr
. Padrãohtml
.
Linguagem de Template Twig
Um arquivo de template do Twig é um arquivo de texto de qualquer extensão contendo marcações e expressões especificas que serão avaliadas e substituídas quando o template for renderizado.
Como vimos no método render
podemos passar variáveis para um template, e para exibi-la utilizamos a marcação {{ }}
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template Twig</title>
</head>
<body>
Nome: {{ nome }}
</body>
</html>
Uma variável passada para um template pode ser uma variável simples, um array ou um objeto, sendo possível acessar o elemento do array ou um atributo de objeto através da sintaxe “var.foo”.
A sintaxe var.foo
é avaliada da seguinte forma:
- verifica se
var
é um array efoo
é um elemento válido; - senão, e se
var
é um objeto, verifica sefoo
é uma propriedade válida; - senão, e se
var
é um objeto, verifica sefoo
é um método válido; - senão, e se
var
é um objeto, verifica segetFoo
é um método válido; - senão, e se
var
é um objeto, verifica seisFoo
é um método válido; - senão, e se
var
é um objeto, verifica sehasFoo
é um método válido; - senão, retorna um valor
null
.
Podemos atribuir um valor para uma variável dentro do template através do seguinte bloco.
{% set foo = 'foo' %}
Variáveis também podem ter filtros que modificam seu conteúdo. Filtros são adicionados ao lado do nome da variável separado por um pipe ” | ” e podem serem chamados em cadeia.
{{ titulo|striptags|title }}
No exemplo acima a variável titulo passa pelo filtro striptags
que remove todas as tags e pelo filtro title
que aplica um “title-cases“.
{{ data |date("d/m/Y") }}
No exemplo acima é aplicado um filtro de formatação de data em um objeto DateTime
.
{{ arr |join(",") }}
No exemplo acima o filtro junta os elementos da variável de array arr
separando os elementos com “,”
Outros filtros podem ser encontrados na documentação.
Estruturas de Controle
Estruturas de controle/decisão de um template como if
/elseif
/else
, loop for
e blocos aparecem dentro de blocos delimitados com a marcação {% ... %}
Vejamos um exemplo de um if
:
{% if user.role == 'admin' %}
<a href="#">Editar</a>
{% endif %}
Ou um com if
/else
:
{% if user.avatar is not empty %}
<img src="{{user.avatar}}" alt="{{user.nome}}">
{% else %}
<img src="avatar-default.png" alt="Avatar Default">
{% endif %}
Como você pode ter visto usamos um operador de comparação diferente, o is
, que permite realizar um teste sobre uma expressão como:
{% if number is odd %} # se um número é impar
{% if foo.attribute is same as(false) %} # se o atributo é realmente false
{% if users is iterable %} # users é um objeto iterable
e suas negações {% if count is not even %}
.
Outro operador útil em muitos testes é o in
, permitindo testar se um valor esta contido. Exemplo:
{% if $letra in ['a', 'e', 'i', 'o','u'] %}
{% if 'dr' in 'Rodrigo' %}
{% if $value not in [1, 3, 5, 7, 9, 13] %}
De forma similar temos operadores para ver verificar se uma string inicia ou termina com um valor:
{% if 'Boteco Digital' starts with 'B' %}
{% if 'Boteco Digital' endswith 'Digital' %}
Ou se corresponde a uma expressão regular:
{% if user.cpf matches '/[0-9]{3}.[0-9]{3}.[0-9]{3}-[0-9]{2}/' %}
Podemos também utilizar os os operadores lógicos and
e or
no lugar de &&
e ||
.
Também é permitido utilizar filtros em estruturas if
como:
{% if user.role|upper == 'ADMIN' %}
Laço
Utilizamos estruturas de controle de loop através do for
{% for user in users %}
<li>{{ user.nome }}</li>
{% endfor %}
Caso não estivermos percorrendo um objeto iterável como um array e precisamos simplesmente de um laço controlado por contador podemos gerar os valores através da função range
, que aceita como parâmetros pelo menos dois valores, valor inicial e final, podendo aceitar o terceiro o incremento.
{% for x in range(1, 10) %}
{% for x in range(1, 10, 2) %}
{% for x in range(10, 1, -1) %}
Ou ainda utilizar o operador ..
{% for x in 1..10 %}
Herança
O recurso que mais motiva a se utilizar uma template engine é seu sistema de herança, onde nos permite criar um esqueleto da página base com todas as áreas comuns delimitando áreas/blocos onde cada página especifica irá inserir seu conteúdo. Vejamos um template base bem simples.
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %} - Template</title>
{% block head %}{% endblock %}
</head>
<body>
<div class="content">
{% block content %}{% endblock %}
</div>
<div>
{% block footer %}
© Copyright 2021 po <a href="https://www.botecodigital.dev.br/">Boteco Digital</a>.
{% endblock %}
</div>
</body>
</html>
Utilizamos o bloco {% block <name> %} ... {% endblock %}
para estipular áreas onde os “templates filhos” irão substituir o conteúdo pelos seus próprios. O {% block <name> %}
pode ser vazio ou possuir um conteúdo padrão que será exibido se nenhum conteúdo for definido no template filho, se necessário pode-se utilizar a função parent()
para recuperar o conteúdo do bloco definido no pai e inserir no bloco do filho que o irá substituir.
No “template filho” começamos herdando o template através do comando {% extends '<template-pai>'%}
, e após, simplesmente definimos os blocos que irão substituir as áreas definidas no template pai uma por uma, não importando a ordem que foram definidos. Lembrando que o nome do bloco deve ser igual tanto no template filho quanto no pai.
{% extends 'base.html' %}
{% block title%} Home {% endblock %}
{% block head%}
<style>
h1{
color: red;
}
</style>
{% endblock%}
{% block content %}
<h1>Conteúdo da página</h1>
<p>Nam nec erat venenatis, eleifend leo nec, mollis lorem. </p>
<p>Vivamus at est at purus condimentum sagittis nec eu augue.</p>
<p>Integer et diam vitae leo lobortis luctus. </p>
<p>Morbi lacinia odio sit amet enim luctus sodales.</p>
<p>In ut finibus sem, vel dictum risus. </p>
{% endblock %}
Em alguns casos pode ser necessário isolar um conteúdo em um arquivo separado e depois inclui-lo em um template, isso pode ser feito facilmente com um include.
{{ include('sidebar.html') }}
Podemos adicionar um comentário dentro do template através do seguinte bloco
{#
Sem comentários
#}
Fica um exemplo mínimo de uso do Twig.
Bom essa foi uma introdução do Twig para ter uma visão geral, mais detalhes como sempre não deixe de dar uma olhada na documentação.
T++ !