Aller au menu - Aller au contenu

Icône Le moteur de template Twig

Avatar
Mise à jour : 02/05/2012
Difficulté : Facile Facile Durée d'étude : 1 heure Creative Commons BY-NC-SA
20 752 visites depuis 7 jours, dont 1 045 sur ce chapitre classé 17/786
Les templates vont nous permettre de séparer le code PHP du code HTML/XML/Text/, etc. Intéressé ? Lisez la suite. ;)
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Les templates Twig

Intérêt


Les templates sont très intéressants. Nous l'avons déjà vu, leur objectif est de séparer le code PHP du code HTML. Ainsi, lorsque vous faites du PHP, vous n'avez pas 100 balises HTML qui gênent la lecture de votre code PHP. De même, lorsque votre designer fait du HTML, il n'a pas à subir votre code barbare PHP auquel il ne comprend rien.

Seulement, pour faire du HTML de présentation, on a toujours besoin d'un peu de code dynamique : faire une boucle pour afficher tous les articles d'un blog, créer des conditions pour afficher un menu différent pour les utilisateurs authentifiés ou non, etc. Pour faciliter ce code dynamique dans les templates, le moteur de template Twig offre son pseudo-langage à lui. Ça n'est pas du PHP, mais c'est plus adapté et voici pourquoi :
  • la syntaxe est plus concise et plus claire. Rappelez-vous, pour afficher une variable, {{ mavar }} suffit, alors qu'en PHP, il faudrait faire <?php echo $mavar; ?> ;
  • il y a quelques fonctionnalités en plus, comme l'héritage de templates (nous le verrons). Cela serait bien entendu possible en PHP, mais il faudrait coder soi-même le système et cela ne serait pas aussi esthétique ;
  • il sécurise vos variables automatiquement : plus besoin de se soucier de <?php htmlentities() ou <?php addslashes() ou que sais-je encore.

Pour ceux qui se posent la question de la rapidité : aucune inquiétude ! Oui il faut transformer le langage Twig en PHP avant de l'exécuter pour, finalement, afficher notre contenu. Mais Twig ne le fait que la première fois et met en cache du code PHP simple afin que, dès la 2e exécution de votre page, ce soit en fait aussi rapide que du PHP simple.


Des pages Web mais aussi des e-mails et autres


En effet, pourquoi se limiter à nos pages HTML ? Les templates peuvent (et doivent) être utilisés partout. Quand on enverra des e-mails, le contenu de l'e-mail sera placé dans un template. Il existe bien sûr un moyen de récupérer le contenu d'un template sans l'afficher immédiatement. Ainsi, en récupérant le contenu du template dans une variable quelconque, on pourra le passer à la fonction mail de notre choix.

Mais il en va de même pour un flux RSS par exemple ! Si l'on sait afficher une liste des news de notre site en HTML grâce au template liste_news.html.twig, alors on saura afficher un fichier RSS en gardant le même contrôleur, mais en utilisant le template liste_news.rss.twig à la place.

En pratique


On a déjà créé un template, mais un rappel ne fait pas de mal. Depuis le contrôleur, voici la syntaxe pour retourner une réponse HTTP toute faite, dont le contenu est celui d'un certain template :

Code : PHP
1
2
3
4
5
6
7
<?php
// Depuis un contrôleur

return $this->render('SdzBlogBundle:Blog:index.html.twig', array(
    'var1' => $var1,
    'var2' => $var2
));

Et voici comment, au milieu d'un contrôleur, récupérer le contenu d'un template en texte :
Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
// Depuis un contrôleur

$contenu = $this->renderView('SdzBlogBundle:Blog:email.txt.twig', array(
    'var1' => $var1,
    'var2' => $var2
));

// Puis on envoie l'e-mail, par exemple :
mail('moi@siteduzero.com', 'Inscription OK', $contenu);

Et le template SdzBlogBundle:Blog:email.txt.twig contiendrait par exemple :

Code : Django
1
2
3
4
5
6
7
{# src/Sdz/BlogBundle/Resources/views/Blog/email.txt.twig #}

Bonjour {{ pseudo }},

Toute l'équipe du site se joint à moi pour vous souhaiter la bienvenue sur notre site !

Revenez nous voir souvent !


L'objectif de la suite de ce chapitre est double :
  • d'abord, vous donner les outils pour faire un code dynamique de base : savoir faire des boucles, des conditions, appliquer des filtres aux variables, etc. ;
  • ensuite, vous donner les outils pour organiser vos templates grâce à l'héritage et à l'inclusion de templates. Ainsi vous aurez un template maître qui contiendra votre design (avec les balises <html>, <head>, etc.) et vos autres templates ne contiendront que le contenu de la page (liste des news, etc.).

La syntaxe de base

À savoir


Première chose à savoir sur Twig : vous pouvez afficher des variables et pouvez exécuter des expressions. Ça n'est pas la même chose.

Variable


Afficher une variable en appliquant un filtre ou non se fait avec les doubles accolades « {{ ... }} ». Voici quelques exemples.

Afficher une variable simple :
Code : Autre
1
Pseudo : {{ pseudo }}

Afficher l'index d'une variable de type Array (un tableau) :
Code : Autre
1
Identifiant : {{ user['id'] }}

Afficher l'attribut d'un objet, dont le getter respecte la convention $objet->getAttribut() :
Code : Autre
1
2
Identifiant : {{ user.id }}
Équivalent : {{ user->getId() }}

Utiliser un filtre. Ici, « upper » met tout en majuscules :
Code : Autre
1
Pseudo en lettres majuscules : {{ pseudo|upper }}

Combiner les filtres. Ici, « striptags » supprime les balises et « title » met la première lettre de chaque mot en majuscule. Remarque : les filtres s'appliquent de gauche à droite. Ici striptags va s'appliquer au texte, puis title sera appliqué. L'équivalent PHP serait (en supposant que les fonctions existent) : title(striptags($news->getTexte())).
Code : Autre
1
Message : {{ news.texte|striptags|title }}

Utiliser un filtre avec des arguments (attention ici, il faut que news['date'] soit un objet de type Datetime) :
Code : Autre
1
Date : {{ news.date|date('d/m/Y') }}


Expressions


De l'autre côté, vous avez les structures dont la syntaxe ressemble à « {% ... %} ». Voici quelques exemples.
Condition simple :
Code : Autre
1
2
3
{% if pseudo == 'winzou' %}
    OK accès autorisé
{% endif %}

Boucle simple :
Code : HTML
1
2
3
4
5
<ul>
  {% for user in users %}
    <li>{{ user.pseudo }}</li>
  {% endfor %}
</ul>

Définition d'une variable depuis le template :
Code : Autre
1
{% set foo = 'bar' %}


Les filtres utiles



Upper


{{ var|upper }} met toutes les lettres de {{ var }} en majuscules.

Striptags


{{ var|striptags }} supprime tous les tags XML de {{ var }} (et supprime les espaces consécutives également).

Date


{{ var|date('d/m/Y') }} formate la date {{ var }} suivant le format donné en argument. La variable {{ var }} peut valoir tout ce que Datetime peut prendre en argument. Par exemple, ce code affichera la date d'aujourd'hui : {{ "now"|date('d/m/y') }}.

Format


{{ "Il y a %s pommes et %s poires dans le jardin"|format(153, nb_poires) }} remplace %s par les arguments tout comme printf le fait.

Length


{{ var|length }} retourne le nombre d'éléments du tableau si {{ var }} est un tableau, et le nombre de caractères si {{ var }} est une chaîne de caractères.

Les autres


Il en existe d'autres, et vous pourrez aussi en créer vous-même. La liste exhaustive se trouve dans la documentation de Twig accessible à cette adresse : http://www.twig-project.org/doc/templa [...] lt-in-filters.

Les expressions utiles



For


Parcourir un tableau :
Code : HTML
1
2
3
4
5
<ul>
  {% for user in users %}
    <li>{{ user.pseudo }}</li>
  {% endfor %}
</ul>

Parcourir un tableau et avoir accès aux clés :
Code : HTML
1
2
3
4
5
<ul>
  {% for key, user in users %}
    <li>{{ user.pseudo }} (id: {{ key }})</li>
  {% endfor %}
</ul>

Parcourir un tableau et gérer le cas où le tableau serait vide (raccourci bien pratique) :
Code : HTML
1
2
3
4
5
6
7
<ul>
  {% for user in users %}
    <li>{{ user.pseudo }}</li>
  {% else %}
    <li><em>Aucun utilisateur</em></li>
  {% endfor %}
</ul>


If


(Exemple repris de la documentation, il me fait marrer.)
Code : Autre
1
2
3
4
5
6
7
{% if kenny.sick %}
    Kenny is sick.
{% elseif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}


Les tests utiles



Is defined


Pour vérifier qu'une variable existe :
Code : Autre
1
2
3
{% if foo is defined %}
    ...
{% endif %}


Is even / Is odd


Pour vérifier qu'un nombre est pair (even) ou impair (odd) :
Code : Autre
1
2
3
{% if foo is even %}
    ...
{% endif %}


Les autres


La liste complète se trouve aussi dans la documentation : http://twig.sensiolabs.org/doc/filters/index.html .


La sécurité avant tout !


Dans tous les exemples précédents, vos variables ont été protégées par Twig ! Twig applique par défaut un filtre sur toutes les variables que vous affichez, afin de protéger de balises HTML malencontreuses. Ainsi, si le pseudo d'un de vos membres contient un "<" par exemple, lorsque vous faites {{ pseudo }} celui ci est échappé, et le texte affiché est en réalité "mon&lt;pseudo" au lieu de "mon<pseudo". Très pratique ! Et donc à savoir : inutile de protéger vos variables en amont, Twig s'occupe de tout en fin de chaîne !

Dans le cas où vous voulez afficher volontairement une variable qui contient du HTML, et que vous ne voulez pas que Twig l'échappe, il vous faut utiliser le filtre raw comme suit : {{ ma_variable_html|raw }}. Avec ce filtre, Twig désactivera localement la protection HTML, et affichera la variable en brut, quelque soit ce qu'elle contient.

Hériter et inclure des templates

L'héritage de template


En voici une partie intéressante ! Plus que la précédente en tout cas. :p
Je vous ai fait un teaser plus haut : l'héritage de templates va nous permettre de résoudre la problématique : « J'ai un seul design et n'ai pas l'envie de le répéter sur chacun de mes templates ». C'est un peu comme ce que vous devez faire aujourd'hui avec les <?php include(), mais en mieux !

Le principe


Le principe est simple : vous avez un template père qui contient le design de votre site ainsi que quelques trous (appelés blocks en anglais, que nous nommerons « blocs » en français) et des templates fils qui vont remplir ces blocs. Les fils vont donc venir hériter du père en remplaçant certains éléments par leur propre contenu.

L'avantage est que les templates fils peuvent modifier plusieurs blocs du template père. Avec la technique des <?php include(), un template inclus ne pourra pas modifier le template père dans un autre endroit que là où il est inclus !

Les blocs classiques sont le centre de la page et le titre. Mais en fait, c'est à vous de les définir ; vous en ajouterez donc autant que vous voudrez.

La pratique


Voici à quoi peut ressembler un template père (appelé plus communément layout). Mettons-le dans src/Sdz/BlogBundle/Resources/views/layout.html.twig :
Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{# src/Sdz/BlogBundle/Resources/views/layout.html.twig #}

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>{% block title %}SdzBlog{% endblock %}</title>
    </head>
    <body>

        {% block body %}
        {% endblock %}

    </body>
</html>

Voici un de nos templates fils. Mettons-le dans src/Sdz/BlogBundle/Resources/views/Blog/index.html.twig :
Code : HTML & Django
1
2
3
4
5
6
7
8
9
{# src/Sdz/BlogBundle/Resources/views/Blog/index.html.twig #}

{% extends "SdzBlogBundle::layout.html.twig" %}

{% block title %}{{ parent() }} - Index{% endblock %}

{% block body %}
    OK, même s'il est pour l'instant un peu vide, mon blog sera trop bien !
{% endblock %}


Qu'est-ce que l'on vient de faire ?


Pour bien comprendre tous les concepts utilisés dans cet exemple très simple, détaillons un peu.

Le nom du template père


On a placé ce template dans views/layout.html.twig et non dans views/qqch/layout.html.twig. C'est tout à fait possible ! En fait, il est inutile de mettre les templates qui ne concernent pas un contrôleur particulier et qui peuvent être réutilisés par plusieurs contrôleurs dans un sous-répertoire. Attention à la notation pour accéder à ce template : du coup, ça n'est plus SdzBlogBundle:MonController:layout.html.twig, mais SdzBlogBundle::layout.html.twig. C'est assez intuitif, en fait : on enlève juste la partie qui correspond au répertoire MonController. C'est ce que l'on a fait à la première ligne du template fils.

La balise « {% block %} » côté père


Pour définir un « trou » (dit bloc) dans le template père, nous avons utilisé la balise {% block %}. Un bloc doit avoir un nom afin que le template fils puisse modifier tel ou tel bloc de façon nominative.
La base, c'est juste de faire {% block nom_du_block %}{% endblock %} et c'est ce que nous avons fait pour le « body ».
Mais vous pouvez insérer un texte par défaut dans les blocs, comme on l'a fait pour le titre. C'est utile pour deux cas de figure :
  • lorsque le template fils ne redéfinit pas ce bloc. Plutôt que de n'avoir rien d'écrit, vous aurez cette valeur par défaut ;
  • lorsque les templates fils veulent réutiliser une valeur commune. Par exemple, si vous souhaitez que le titre de toutes les pages de votre site commence par « SdzBlog », alors depuis les templates fils, vous pouvez utiliser {{ parent() }} qui permet d'utiliser le contenu par défaut du bloc côté père. Regardez, nous l'avons fait pour le titre dans le template fils.

La balise « {% block %} » côté fils


Elle se définit exactement comme dans le template père sauf que cette fois-ci, on y met notre contenu.

Mais étant donné que les blocs se définissent et se remplissent de la même façon, vous avez pu deviner qu'on peut hériter en cascade ! En effet, si l'on crée un troisième template petit-fils qui hérite de fils, on pourra faire beaucoup de choses.

Le modèle « triple héritage »


Pour bien organiser ses templates, une bonne pratique est sortie du lot. Il s'agit de faire de l'héritage de template sur trois niveaux, chacun des niveaux remplissant un rôle particulier. Les trois templates sont les suivants :
  • layout général : c'est le design de votre site, indépendamment de vos bundles. Il contient le header, le footer, etc. La structure de votre site donc (c'est notre template père) ;
  • layout du bundle : il hérite du layout général et contient les parties communes à toutes les pages d'un même bundle. Par exemple, pour notre blog, on pourrait afficher un menu particulier, rajouter « Blog » dans le titre, etc. ;
  • template de page : il hérite du layout du bundle et contient le contenu central de votre page.

Nous verrons un exemple de ce triple héritage juste après dans l'exemple du blog.

Question : puisque le layout général ne dépend pas d'un bundle en particulier, où le mettre ?

Réponse : dans votre répertoire app/ ! En effet, dans ce répertoire, vous pouvez toujours avoir des fichiers qui écrasent ceux des bundles ou bien des fichiers communs aux bundles. Le layout général de votre site fait partie de ces ressources communes. Son répertoire exact doit être app/Resources/views/layout.html.twig.

Et pour l'appeler depuis vos templates, la syntaxe est la suivante : ::layout.html.twig. Encore une fois, c'est très intuitif : juste après avoir enlevé le nom du contrôleur tout à l'heure, on enlève juste cette fois-ci le nom du bundle.

Afin de bien vous représenter l'architecture adoptée, je vous propose un petit schéma. Il vaut ce qu'il vaut, mais vous permet de bien comprendre ce qu'on fait :

Image utilisateur


L'inclusion de templates


La théorie : quand faire de l'inclusion ?


Hériter, c'est bien, mais inclure, cela n'est pas mal non plus. Prenons un exemple pour bien faire la différence.

Le formulaire pour ajouter un article est le même que celui pour… modifier un article. On ne va pas faire du copier-coller de code, cela serait assez moche, et puis nous sommes fainéants. C'est ici que l'inclusion de templates intervient. On a nos deux templates SdzBlogBundle:Blog:ajouter.html.twig et SdzBlogBundle:Blog:modifier.html.twig qui héritent chacun de SdzBlogBundle::layout.html.twig. L'affichage exact de ces deux templates diffère un peu, mais chacun d'eux inclut SdzBlogBundle:Blog:form.html.twig à l'endroit exact pour afficher le formulaire.

On voit bien qu'on ne peut pas faire d'héritage sur le template form.html.twig car il faudrait le faire hériter une fois de ajouter.html.twig, une fois de modifier.html.twig, etc. Comment savoir ? Et si un jour nous souhaitons ne le faire hériter de rien du tout pour afficher le formulaire tout seul dans une popup par exemple ? Bref, c'est bien une inclusion qu'il nous faut ici.

La pratique : comment le faire ?


Comme toujours avec Twig, cela se fait très facilement. Il faut utiliser la balise {% include %}, comme ceci : {% include "SdzBlogBundle:Blog:form.html.twig" %}. Ce code inclura le contenu du template à l'endroit de la base. Une sorte de copier-coller automatique, en fait ! Exemple tiré de SdzBlogBundle:Blog:ajouter.html.twig :

Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{# src/Sdz/BlogBundle/Resources/views/Blog/ajouter.html.twig #}

{% extends "SdzBlogBundle::layout.html.twig" %}

{% block body %}

  <h2>Ajouter un article</h2>

  {% include "SdzBlogBundle:Blog:formulaire.html.twig" %}

  <p>
     Attention : cet article sera ajouté directement
     sur la page d'accueil après validation du formulaire.
  </p>

{% endblock %}

Application : les templates de notre blog

Revenons à notre blog. Faites en sorte d'avoir sous la main le contrôleur que l'on a réalisé au chapitre précédent. Le but ici est de créer tous les templates que l'on a utilisés depuis le contrôleur, ou du moins leur squelette. Étant donné que l'on n'a pas encore la vraie liste des articles, on va faire avec des variables vides : ça va se remplir par la suite, mais le fait d'employer des variables vide va nous permettre dès maintenant de construire le template.

Pour encadrer tout ça, nous allons utiliser le modèle d'héritage sur trois niveaux : layout général, layout du bundle et template.

Layout général


La théorie


Comme évoqué plus tôt, le layout est la structure HTML de notre site avec des blocs aux endroits stratégiques pour permettre aux templates qui hériteront de ce dernier de personnaliser la page. On va ici créer une structure simple ; je vous laisse la personnaliser si besoin est. Pour les blocs, pareil pour l'instant, on fait simple : un bloc pour le body et un bloc pour le titre.

Encore une fois, vous devez personnaliser tout ça ! Je ne suis pas là pour faire le blog à votre place, juste pour vous guider. Si vous ne pratiquez pas de votre côté en ajoutant, supprimant et améliorant tout ce que l'on voit ici, vous serez vite perdu ! Je vous fais confiance, ne faites pas que lire, codez. :)


Je vais également en profiter pour introduire l'utilisation de ressources CSS/JS/etc dans Symfony2. Cela se fait très bien avec la balise asset de Twig, qui va chercher vos ressources dans le répertoire /web. Regardez simplement comment elle est utilisée dans l'exemple et vous saurez l'utiliser de façon basique.

Pour le design du blog que l'on va construire, je vais utiliser le bootstrap de Twitter. C'est un framework CSS, l'équivalent pour le CSS de ce que Symfony2 est pour le PHP. Cela permet de faire des beaux designs, boutons ou liens très rapidement. Vous pourrez le voir dans les vues que je fais par la suite. Mais tout d'abord vous devez le télécharger ici : http://twitter.github.com/bootstrap/assets/bootstrap.zip et l'extraire dans le répertoire web/. Vous devez avoir le fichier bootstrap.css disponible dans le répertoire web/css/bootstrap.css par exemple.


La pratique


Commençons par faire le layout général de l'application, la vue située dans le répertoire app/. Voici le code exemple que je vous propose :
Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{# app/Resources/views/layout.html.twig #}

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

		<title>{% block title %}Sdz{% endblock %}</title>

		{% block stylesheets %}
			<link rel="stylesheet" href="{{ asset('css/bootstrap.css') }}" type="text/css" />
		{% endblock %}
	</head>

	<body>
		<div class="container">
			<div id="header" class="hero-unit">
				<h1>Mon Projet Symfony2</h1>
				<p>Ce projet est propulsé par Symfony2, et construit grâce au tutoriel du siteduzero.</p>
				<p><a class="btn btn-primary btn-large" href="http://www.siteduzero.com/tutoriel-3-517569-symfony2.html">
					Lire le tutoriel &raquo;
				</a></p>
			</div>

			<div class="row">
				<div id="menu" class="span3">
					<h3>Le blog</h3>
					<ul class="nav nav-pills nav-stacked">
						<li><a href="{{ path('sdzblog_accueil') }}">Accueil du blog</a></li>
						<li><a href="{{ path('sdzblog_ajouter') }}">Ajouter un articlce</a></li>
					</ul>
				</div>
				<div id="content" class="span9">
					{% block body %}
					{% endblock %}
				</div>
			</div>

			<hr>

			<footer>
				<p>The sky's the limit &copy; 2012 and beyond.</p>
			</footer>
		</div>
	</body>

	{% block javascripts %}
		{# Ajoutez ce javascript si vous comptez vous servir des fonctionnalités du bootstrap Twitter #}
		<script type="text/javascript" src="{{ asset('js/bootstrap.js') }}"></script>
	{% endblock %}

</html>

J'ai surligné les parties qui contiennent un peu de Twig :
  • ligne 8 : création du bloc « title » avec « Sdz » comme contenu par défaut ;
  • lignes 11 : appel du CSS situé dans /web/css/bootstrap.css ;
  • lignes 29 et 30 : utilisation de la fonction {{ path }} pour faire des liens vers d'autres routes ;
  • lignes 34 et 35 : création du bloc « body » sans contenu par défaut.

Et voilà, nous avons notre layout général ! Pour pouvoir tester nos pages, il faut maintenant s'attaquer au layout du bundle.

Layout du bundle


La théorie


Comme on l'a dit, ce template va hériter du layout général, rajouter la petite touche perso au bundle Blog, puis se faire hériter à son tour par les templates finaux. En fait, il ne contient pas grand-chose. Laissez courir votre imagination, mais moi, je ne vais rajouter qu'une balise <h1>, vous voyez ainsi le mécanisme et pouvez personnaliser à votre sauce.
La seule chose à laquelle il faut faire attention, c'est au niveau du nom des blocs. Une bonne pratique consiste à préfixer le nom des blocs par le nom du bundle courant. Regardez le code et vous comprendrez.

La pratique


Extrait du fichier src/Sdz/BlogBundle/Resources/views/layout.html.twig :
Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{# src/Sdz/BlogBundle/Resources/views/layout.html.twig #}

{% extends "::layout.html.twig" %}

{% block title %}
	Blog - {{ parent() }}
{% endblock %}

{% block body %}

	{# On définit un sous-titre commun à toutes les pages du bundle, par exemple #}
	<h1>Blog</h1>

	<hr>

	{# On gère les messages flash de façon global pour tout le bundle #}
	{% for key, flash in app.session.getFlashes() %}
		<div class="alert alert-{{ key }}">
			{{ flash }}
		</div>
	{% endfor %}

	{# On définit un nouveau block, que les vues du bundle pourront étendre #}
	{% block sdzblog_body %}
	{% endblock %}

{% endblock %}

On a ajouté un <h1> dans le bloc « body » puis créé un nouveau bloc qui sera personnalisé par les templates finaux. On a préfixé le nom du nouveau block pour le body afin d'avoir un nom unique pour notre bundle.

Les templates finaux


La théorie


Pas trop de théorie ici, il ne reste plus qu'à hériter et personnaliser les templates.

Blog/index.html.twig


C'est le template de la page d'accueil. On va faire notre première boucle sur la variable {{ articles }}. Cette variable n'existe pas encore, on va modifier le contrôleur juste après.

Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{# src/Sdz/BlogBundle/Resources/views/Blog/index.html.twig #}

{% extends "SdzBlogBundle::layout.html.twig" %}

{% block title %}
	Accueil - {{ parent() }}
{% endblock %}

{% block sdzblog_body %}

	<h2>Liste des articles</h2>

	<ul>
		{% for article in articles %}
			<li>
				<a href="{{ path('sdzblog_voir', {'id': article.id}) }}">{{ article.titre }}</a>
				par {{ article.auteur }},
				le {{ article.date|date('d/m/Y') }}
			</li>
		{% else %}
			<li>Pas (encore !) d'articles</li>
		{% endfor %}
	</ul>

{% endblock %}

Pas grand-chose à dire, on a juste utilisé les variables et expressions expliquées plus haut dans ce chapitre.

Afin que cette page fonctionne, il nous faut modifier l'action indexAction() du contrôleur pour passer une variable {{ articles }} à cette vue. Pour l'instant, voici juste de quoi se débarrasser de l'erreur :
Code : PHP
1
2
3
4
5
6
7
<?php
// src/Sdz/BlogBundle/Controller/BlogController.php

// Dans l'action indexAction() :
return $this->render('SdzBlogBundle:Blog:index.html.twig', array(
	'articles' => array()
));

Vous pouvez dès maintenant voir votre nouvelle peau : http://localhost/Symfony/web/app_dev.php/blog !


Si vous avez bien ajouté le CSS de Twiter, voici ce à quoi cela devrait ressembler :

Image utilisateur


Vous voulez voir des articles au lieu du message pas très drôle ? Je suis trop bon, voici un tableau d'articles à ajouter temporairement dans la méthode indexAction(), que vous pouvez passer en paramètre à la méthode render(). C'est un tableau pour l'exemple, par la suite il faudra bien sûr récupérer les articles depuis la base de données ;) .

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// src/Sdz/BlogBundle/Controller/BlogController.php

// ...

public function indexAction()
{
    // ...

    // Les articles :
    $articles = array(
        array('titre' => 'Mon weekend a Phi Phi Island !',          'id' => 1, 'auteur' => 'winzou',  'contenu' => 'Ce weekend était trop bien. Blabla...',  'date' => new \Datetime()),
        array('titre' => 'Repetition du National Day de Singapour', 'id' => 2, 'auteur' => 'winzou',  'contenu' => 'Bientôt prêt pour le jour J. Blabla...', 'date' => new \Datetime()),
        array('titre' => 'Chiffre d\'affaire en hausse',            'id' => 3, 'auteur' => 'M@teo21', 'contenu' => '+500% sur 1 an, fabuleux. Blabla...',    'date' => new \Datetime())
    );
    
    // Puis modifiez la ligne du render comme ceci, pour prendre en compte nos articles :
    return $this->render('SdzBlogBundle:Blog:index.html.twig', array(
        'articles' => $articles
    ));
}

Rechargez la page, et profitez du résultat. ;)

Attention, on vient de définir des articles en brut dans le contrôleur, mais c'est uniquement pour l'exemple d'utilisation de Twig ! Ce n'est bien sûr pas du tout une façon correcte de le faire, par la suite nous les récupérerons depuis la base de données.


Blog/voir.html.twig


Il ressemble beaucoup à index.html.twig sauf qu'on passe à la vue une variable {{ article }} contenant un seul article, et non plus une liste d'articles. Voici un code par exemple :

Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{# src/Sdz/BlogBundle/Resources/views/Blog/voir.html.twig #}

{% extends "SdzBlogBundle::layout.html.twig" %}

{% block title %}
	Lecture d'un article - {{ parent() }}
{% endblock %}

{% block sdzblog_body %}

	<h2>{{ article.titre }}</h2>
	<i>Par {{ article.auteur }}, le {{ article.date|date('d/m/Y') }}</i>

	<div class="well">
		{{ article.contenu }}
	</div>

	<p>
		<a href="{{ path('sdzblog_accueil') }}" class="btn">
			<i class="icon-chevron-left"></i>
			Retour à la liste
		</a>
		<a href="{{ path('sdzblog_modifier', {'id': article.id}) }}" class="btn">
			<i class="icon-edit"></i>
			Modifier l'article
		</a>
		<a href="{{ path('sdzblog_supprimer', {'id': article.id}) }}" class="btn">
			<i class="icon-trash"></i>
			Supprimer l'article
		</a>
	</p>

{% endblock %}


Et l'adaptation du contrôleur bien évidemment :

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
// src/Sdz/BlogBundle/Controller/BlogController.php

// ...

public function voirAction($id)
{
    // ...

    $article = array(
        'id'      => 1,
        'titre'   => 'Mon weekend a Phi Phi Island !',
        'auteur'  => 'winzou',
        'contenu' => 'Ce weekend était trop bien. Blabla...',
        'date'    => new \Datetime()
    );
    
    // Puis modifiez la ligne du render comme ceci, pour prendre en compte l'article :
    return $this->render('SdzBlogBundle:Blog:voir.html.twig', array(
        'article' => $article
    ));
}

Ainsi que le résultat visuel http://localhost/Symfony/web/app_dev.php/blog/article/1 :

Image utilisateur




Blog/modifier.html.twig et ajouter.html.twig


Ceux-ci contiennent une inclusion de template. En effet, rappelez-vous, j'avais pris l'exemple d'un formulaire utilisé pour l'ajout mais également la modification. C'est notre cas ici, justement. Voici donc le fichier modifier.html.twig :
Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{# src/Sdz/BlogBundle/Resources/views/Blog/modifier.html.twig #}

{% extends "SdzBlogBundle::layout.html.twig" %}

{% block title %}
	Modifier un article - {{ parent() }}
{% endblock %}

{% block sdzblog_body %}

	<h2>Modifier un article</h2>

	{% include "SdzBlogBundle:Blog:formulaire.html.twig" %}

	<p>
		Vous éditez un article déjà existant,
		ne le changez pas trop pour éviter
		aux membres de ne pas comprendre
		ce qu'il se passe.
	</p>

	<p>
		<a href="{{ path('sdzblog_voir', {'id': article.id}) }}" class="btn">
			<i class="icon-chevron-left"></i>
			Retour à l'article
		</a>
	</p>

{% endblock %}


Le template ajouter.html.twig lui ressemble énormément, je vous laisse donc le faire.

Quant à formulaire.html.twig, on ne sait pas le faire encore car il demande des notions de formulaire, mais faisons déjà sa structure pour le moment :

Code : HTML & Django
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{# src/Sdz/BlogBundle/Resources/views/Blog/formulaire.html.twig #}

{# Cette vue n'hérite de personne, elle sera inclue par d'autres vues qui, elles, hériteront sûrement du layout. #}
{# Je dis "sûrement" car, ici pour cette vue, on n'en sait rien et c'est une info qui ne nous concerne pas. #}

<h3>Formulaire d'article</h3>

{# Ici on laisse vide la vue pour l'instant, on la comblera plus tard lorsque saura afficher un formulaire. #}
<div class="well">
	Ici se trouvera le formulaire.
</div>

Une chose importantes ici : dans ce template, il n'y a aucune notion de bloc, d'héritage, etc. Ce template est un électron libre : vous pouvez l'inclure depuis n'importe quel autre template. Il faut seulement lui donner un « with » afin que, ici, la variable du formulaire {{ form }} soit disponible.

Et le résultat visuel http://localhost/Symfony/web/app_dev.php/blog/modifier/1 :

Image utilisateur


Conclusion


Et voilà, nous avons généré presque tous nos templates. Bien sûr, ils sont encore un peu vides car on ne sait pas utiliser les formulaires ni récupérer les articles depuis la base de données. Mais vous savez maintenant les réaliser et c'était une étape importante ! Je vais vous laisser créer les templates manquants ou d'autres afin de vous faire la main. Bon code !
Voilà, c'est terminé pour ce chapitre ; vous savez afficher avec mise en forme le contenu de votre site.

Ce chapitre clôt la première partie du cours. En effet, vous savez presque tout faire maintenant ! Bon OK, c'est vrai, il vous manque encore des concepts clés. Mais vous maîtrisez pleinement la base et rajouter d'autres concepts par-dessus sera bien plus facile, heureusement.

La deuxième partie du cours va nous permettre de découvrir la gestion de la base de données avec Doctrine2, l'outil livré par défaut avec Symfony2.

Le code tel qu'il doit être à la fin de ce chapitre est ici : https://github.com/winzou/SdzBlog/tree [...] 2175090870742 J'y ai notamment intégré les vues ajouter.html.twig et supprimer.html.twig ;)


Pour plus d'informations concernant Twig et ses possibilités, n'hésitez pas à lire la documentation officielle.
Chapitre précédent Sommaire Chapitre suivant

Partager

68 commentaires pour "Le moteur de template Twig"
Note moyenne : 3.75 / 4 (245 votes)
Pseudo Commentaire
Hors ligne winzou # Posté le 29/03/2012 à 10:04:36
lala
Avatar

Avis : Très bon Modérateurs

Ville : Singapour
Pays : Singapour
Études : Ecole Centrale de Lyon

Merci c'est corrigé ;)

Un tutoriel pour débuter avec le framework Symfony2.
Chapitre en beta-test : Déployer son site Symfony2 en production, donnez vos avis !

Je recherche toujours quelqu'un capable de faire des icônes sympas pour les chapitres du tutoriel, contactez-moi, merci !
 
Hors ligne kasmi.aymen # Posté le 30/03/2012 à 11:39:35 Message supprimé pour le motif suivant : Merci de poster vos problèmes sur le forum, pas ici..

Avis : Très bon

j'ai une variable javascript que je veux la passer d'une page twig à une autre je ne sais pas comment ni comment manipuler ceci avec les variable de session symfony2
SVP aidé moi
Hors ligne pomeh # Posté le 19/04/2012 à 11:21:29
Avatar

Hello,

Merci pour le tuto, bien écrit et plutôt simple à comprendre :)

Je viens de "signaler une erreur" via la fonctionnalité prévue pour ça sur le site, je poste ici aussi le message que j'ai envoyé pour infos :)

Citation : pomeh
Une toute petite erreur s'est glissée dans le code HTML (Twig) du fichier app/Resources/views/layout.html.twig. Il contient des balises HTML après la balise </body>, ce qui est incorrect et n'est pas valide W3C.

Code : HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{# app/Resources/views/layout.html.twig #}

<!DOCTYPE html>
<html>
	<head>
		<!-- du contenu -->
	</head>

	<body>
		<div class="container">
			<!-- du contenu -->
		</div>
	</body>
	<!-- Le bloc ci-dessous est mal positionné, il ne devrait pas être après la balise </body> -->
	{% block javascripts %}
		{# Ajoutez ce javascript si vous comptez vous servir des fonctionnalités du bootstrap Twitter #}
		<script type="text/javascript" src="{{ asset('js/bootstrap.js') }}"></script>
	{% endblock %}
	<!-- fin du bloc -->
</html>


Il faudrait remonter le bloc juste avant la balise </body> pour que ce soit correct :)


EDIT: je viens de remarquer un second petit point bloquant, je l'ai également signalé mais je mets le message :)
Citation : pomeh

Une (autre) petite erreur s'est glissée dans le code HTML du fichier app/Resources/views/layout.html.twig. Le code Javascript utilisé par Bootstrap nécéssite d'inclure jQuery dans la page.
Sans cette inclusion, on obtient une erreur du type "$ is not a function" sous Firefox ou "Uncaught TypeError: undefined is not a function" sous Chrome.

Il faut rajouter quelque chose du style:
Code : HTML
1
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

afin que cela fonctionne correctement (à voir quelle est la bonne version de jQuery à utiliser ?).

PS: l'url commençant par "//" dans l'exemple est volontaire, cela permet de fonctionner correctement en HTTP et en HTTPS.

// fin de l'edit


Un second petit point que j'ai du mal à saisir au niveau de l'héritage des templates. Le layout général est censé être utilisable par toute notre application, quels que soit nos bundles utilisés. Le layout du bundle quant à lieu est utilisé par toutes les vues du bundle. Du coup je ne comprends pas pourquoi, dans le layout général, on fais appel aux routes du bundle:

Code : HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{# app/Resources/views/layout.html.twig #}

<!-- [...] -->
<div id="menu" class="span3">
	<h3>Le blog</h3>
	<ul class="nav nav-pills nav-stacked">
		<!-- Le layout général utilise les routes du bundle -->
		<li><a href="{{ path('sdzblog_accueil') }}">Accueil du blog</a></li>
		<li><a href="{{ path('sdzblog_ajouter') }}">Ajouter un articlce</a></li>
	</ul>
</div>
<!-- [...] -->


Des infos sur ce point précis ?

HTML5, javascript/jQuery, php/symfony and C#
Follow me on Twitter
 
Hors ligne winzou # Posté le 19/04/2012 à 19:53:21
lala
Avatar

Avis : Très bon Modérateurs

Ville : Singapour
Pays : Singapour
Études : Ecole Centrale de Lyon

Salut,

Merci pour le placement des JS, je regarderai ça.

Pour ton second point j'ai pas bien compris. Le layout général est comme son nom l'indique, général. Il couvre donc tous les bundles utilisés par l'application, et peut tous les utiliser. Il n'y a donc aucun soucis à utiliser les routes de notre SdzBlogBundle au contraire.

Un tutoriel pour débuter avec le framework Symfony2.
Chapitre en beta-test : Déployer son site Symfony2 en production, donnez vos avis !

Je recherche toujours quelqu'un capable de faire des icônes sympas pour les chapitres du tutoriel, contactez-moi, merci !
 
Hors ligne tsubasa456 # Posté le 20/04/2012 à 02:34:49
Avatar

Avis : Très bon

Salutations !

Merci encore pour ton excellent tuto !

Dans le second point mentionné par "pomeh" je penses qu'il veut parler de l'utilisation du lien
<a href="{{ path('sdzblog_accueil') }}">Accueil du blog</a>
Il se demande pourquoi a t-on le droit d'utiliser la route 'sdzblog_accueil' via le layout général puisqu'il est seulement défini dans le fichier routing.yml de notre bundle (donc à un niveau inférieur d'héritage).

J'aimerais répondre à cette question mais je ne suis pas certains de ma réponse.
On peut utiliser les routes de nos bundles dans le layout général car notre application va préalablement charger toutes les routes de nos bundles via le fichier "app/config/routing.yml". Souviens toi de ce qui a été dis dans la partie "routing", lorsque l'on crée un bundle, on peut décider de l'ajouter directement a notre fichier route principal (celui du répertoire "app").
Je sais pas si c'est bien cette question que tu t'étais posé pomeh mais en tout cas j'espère t'avoir aider ! :)

Pour ma part je voulais savoir si c'est moi ou il y a une erreur sur le screen de la partie "Les templates finaux -> Blog/voir.html.twig". Dans le code il y a un lien pour la suppression de l'article mais on ne le voit pas sur le screen. Pas très grave on me dira xD

Sur ceux merci encore !

Voir tous les commentaires