Grégoire Pineau - Symfony Live - Paris 2013
php composer.phar require twig/twig:~1.12
// twig.php
require __DIR__.'/vendor/autoload.php';
$loader = new Twig_Loader_Filesystem(array(__DIR__.'/templates'));
$twig = new Twig_Environment($loader);
Template:
{# hello.html.twig #}
hello {{ name }}
Php:
// hello.php
require __DIR__.'/twig.php';
echo $twig->render('hello.html.twig', array('name' => 'greg'));
Résultat:
hello greg
Pourquoi ? Quand ?
{# layout.html.twig #}
<html>
<head>
<title>{% block title '' %}</title>
{% block meta '' %}
{% block css %}
<style type="text/css" src="/css/styles.css"></style>
{% endblock %}
</head>
<body>
Navigation
Fil d'arianne
{% block body '' %}
{% block javascripts '' %}
</body>
</html>
{# homepage.html.twig #}
{% extends 'layout.html.twig' %}
{% block title 'Ma page' %}
{% block css %}
{{ parent() }}
<style type="text/css" src="/css/homepage.css"></style>
{% endblock %}
{% block body %}
Coverflow
Boutons
{% endblock %}
<html>
<head>
<title>Ma page</title>
<style type="text/css" src="/css/styles.css"></style>
<style type="text/css" src="/css/homepage.css"></style>
</head>
<body>
Navigation
Fil d'arrianne
Coverflow
Boutons
</body>
</html>
Pourquoi ? Quand ?
{# qui-sommes-nous.html.twig #}
<div>
Qui sommes nous ?
</div>
::
{# homepage.html.twig #}
<div>
New
</div>
<div>
Derniers Commentaires
</div>
{{ include('qui-sommes-nous.html.twig') }}
{# news.html.twig #}
{% for post in posts %}
{{ include('post.html.twig', { post: post }, with_context = false) }}
{% endfor %}
::
{# post.html.twig #}
<div>
<h3>{{ post.title}}</h3>
<div>{{ post.content }}</div>
</div>
Pourquoi ? Quand ?
{# blocks.html.twig #}
{% block qui_sommes_nous %}
<div>
Qui sommes nous ?
</div>
{% endblock %}
::
{# homepage.html.twig #}
{# ... #}
{% use "blocks.html.twig" %}
{{ block('qui_sommes_nous') }}
{# OU #}
{% block qui_sommes_nous %}
<div class="container">
{{ parent() }}
</div>
{% endblock %}
Pourquoi ? Quand ?
{# news.html.twig #}
{% for post in posts %}
{% embed "post.html.twig" with { post: post } only %}{% endembed %}
{% endfor %}
{# post.html.twig #}
<div>
{% block title %}<h3>{{ post.title}}</h3>{% endblock %}
{% block content%}<div>{{ post.content }}</div>{% endblock %}
{% block metas %}
<p>Par {{ post.author }} le {{ post.date }}</p>
{% endblock %}
{% block comments %}
<p>il y a {{ post.comment|length }} commentaires</p>
{% endblock %}
</div>
{# homepage.html.twig #}
<div>
{% embed "embed/post.html.twig" with { post: posts|last } only %}
{% block title %}
<h1>{{ post.title}}</h1>
{% endblock %}
{% block comments %}
{{ parent() }}<p>Lire les commentaire</p>
{% endblock %}
{% endembed %}
</div>
<div>
Derniers Commentaires
</div>
{{ include('include/qui-sommes-nous.html.twig') }}
Pourquoi ? Quand ?
{% macro input(name, type = 'text', value = '', size = '20') %}
{% spaceless %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}"
size="{{ size }}" />
{% endspaceless %}
{% endmacro %}
{% import _self as forms %}
{# ou #}
{% import 'forms.html.twig' as forms %}
{{ forms.input('user') }}
{{ forms.input('email', 'email') }}
Vous voulez:
Note:
Les fonctions twig ne sont pas faites pour générer du HTML ; Il en sera question un peu plus loin
Comment construire un systeme de thème ?
Use case:
themes
├── default
│ ├── footer.html.twig
│ ├── header.html.twig
│ ├── homepage.html.twig
│ └── layout.html.twig
└── my_theme
└── header.html.twig
{# default/layout.html.twig #}
<html>
<head>
<title>{% block title '' %}</title>
</head>
<body>
{{ include('logo.html.twig') }}
{% block content '' %}
</body>
</html>
{# default/homepage.html.twig #}
{% extends 'layout.html.twig' %}
{% block content %}
Du contenu
{% endblock %}
::
{# default/logo.html.twig #}
<img src="logo.png">
{# my_theme/logo.html.twig #}
<h1>Mon site</h1>
<img src="mon-logo.png">
<html>
<head>
<title></title>
</head>
<body>
<h1>Mon site</h1>
<img src="mon-logo.png">
Du contenu
</body>
</html>
require __DIR__.'/vendor/autoload.php';
$loader = new Twig_Loader_Filesystem(array(
__DIR__.'/templates/theming/my_theme',
__DIR__.'/templates/theming/default',
));
$twig = new Twig_Environment($loader);
echo $twig->render('homepage.html.twig'); // <--- /!\ Attention
themes
├── default
│ ├── footer.html.twig
│ ├── header.html.twig
│ ├── homepage.html.twig
│ └── layout.html.twig
└── my_theme
├── homepage.html.twig
└── header.html.twig
{# my_theme/homepage.html.twig #}
{% extends 'default/homepage.html.twig' %}
{% block content %}
<div class="container">
{{ parent() }}
</div>
{% endblock %}
Il faut ajouter le répertoire commun aux deux dossiers de thème :
require __DIR__.'/vendor/autoload.php';
$loader = new Twig_Loader_Filesystem(array(
__DIR__.'/templates/theming/my_theme',
__DIR__.'/templates/theming/default',
__DIR__.'/templates/theming', // <---- HERE
));
$twig = new Twig_Environment($loader);
echo $twig->render('homepage.html.twig');
Mais pourquoi Wordpress n'utilise pas twig pour ses templates ??????????????????
Comment personaliser l'affichage des fonctions twig ?
Use case:
Note: Ici un include
/ embed
ne peut pas suffir, car il y a potentiellement des besoins métiers (appel à la BDD, ...) qui ne peuvent / doivent pas etre fait depuis une template.
{# layout.html.twig #}
{{ display_menu(page|default(null)) }}
::
<!-- Résultat -->
<nav>
<ul>
<li><a href="#">A</a></li>
<li><a href="#">B</a></li>
<li><a href="#">C</a></li>
</ul>
</nav>
display_menu
est une fonction twig qui va générer du contenu HTML.
Nous voulons un support pour display_menu
, display_flash
, display_pagination
, display_slider
, display_*
Pour ajouter ces fonctions à twig, Nous allons:
menu
, flash
, pagination
, slider
, ...display_*
class DisplayExtension extends \Twig_Extension
{
private $themes;
public function __construct(array $themes = array())
{
$this->themes = $themes;
}
}
public function getFunctions()
{
return array(
new \Twig_SimpleFunction(
'display_menu',
array($this, 'displayBlock'),
array('needs_environment' => true, 'is_safe' => array('html'))
),
);
}
public function displayBlock(\Twig_Environment $env, $block = 'menu', $parameters = array())
{
foreach ($this->themes as $theme) {
if ($theme instanceof \Twig_Template) {
$template = $theme;
} else {
$template = $env->loadTemplate($theme);
}
if ($template->hasBlock($block)) {
return $template->renderBlock($block, $parameters);
}
}
throw new \InvalidArgumentException('Unable to find block '.$block);
}
Attention: Ici on utilise une partie de l'API de twig qui n'est pas publique *
$twig->addExtension(new DisplayExtension(array('display.html.twig')));
{# display.html.twig ; Notre template de blocks #}
{% block menu %}
{% spaceless %}
<nav>
<ul>
<li><a href="#">A</a></li>
<li><a href="#">B</a></li>
<li><a href="#">C</a></li>
</ul>
</nav>
{% endspaceless %}
{% endblock %}
{% block pagination %}
{# ... #}
{% block slider %}
{# ... #}
{% block flash %}
{# ... #}
public function getFunctions()
{
return array(
new \Twig_SimpleFunction(
'display_menu',
array($this, 'displayMenu'),
array('needs_environment' => true, 'is_safe' => array('html'))
),
new \Twig_SimpleFunction(
'display_*',
array($this, 'displayBlock'),
array('needs_environment' => true, 'is_safe' => array('html'))
),
);
}
public function displayMenu(\Twig_Environment $env, $page = null)
{
// Do what you need HERE.
return $this->displayBlock($env, 'menu', array('page' => $page));
}
Il faut ajouter un tag, qui va ajouter à la compilation un nouveau thème pour la template courante.
Le code est un peu compliqué, mais il sera dans le dépôt ;)
{# page.html.twig #}
{% extends 'layout.html.twig' %}
{% display_theme _self %}
{% block menu %}
<h6>Menu:</h6>
<nav class="class4">
<ul class="class5">
<li><a href="#">A</a></li>
<li><a href="#">B</a></li>
<li><a href="#">C</a></li>
</ul>
</nav>
{% endblock %}
{{ display_menu(page|default(null)) }}
<!-- Résultat -->
<h6>Menu:</h6>
<nav class="class4">
<ul class="class5">
<li><a href="#">A</a></li>
<li><a href="#">B</a></li>
<li><a href="#">C</a></li>
</ul>
</nav>
Les slides:
Sinon:
Et: