Вы находитесь на странице: 1из 256

The Book

Version: 2.6
generated on January 14, 2015

The Book (2.6)


This work is licensed under the Attribution-Share Alike 3.0 Unported license (http://creativecommons.org/
licenses/by-sa/3.0/).
You are free to share (to copy, distribute and transmit the work), and to remix (to adapt the work) under the
following conditions:
Attribution: You must attribute the work in the manner specified by the author or licensor (but
not in any way that suggests that they endorse you or your use of the work).
Share Alike: If you alter, transform, or build upon this work, you may distribute the resulting work
only under the same, similar or a compatible license. For any reuse or distribution, you must make
clear to others the license terms of this work.
The information in this book is distributed on an as is basis, without warranty. Although every precaution
has been taken in the preparation of this work, neither the author(s) nor SensioLabs shall have any liability to
any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by
the information contained in this work.
If you find typos or errors, feel free to report them by creating a ticket on the Symfony ticketing system
(http://github.com/symfony/symfony-docs/issues). Based on tickets and users feedback, this book is
continuously updated.

Contents at a Glance
Les fondamentaux de Symfony2 et HTTP ............................................................................................4
Symfony2 versus PHP pur .................................................................................................................14
Installer et Configurer Symfony .........................................................................................................27
La cration de pages avec Symfony2 ..................................................................................................34
Le Contrleur ...................................................................................................................................48
Routage ............................................................................................................................................61
Crer et utiliser les templates .............................................................................................................74
Doctrine et les bases de donnes........................................................................................................93
Propel et les bases de donnes ......................................................................................................... 117
Les Tests......................................................................................................................................... 125
Validation....................................................................................................................................... 139
Formulaires .................................................................................................................................... 148
La scurit ...................................................................................................................................... 174
Le Cache HTTP .............................................................................................................................. 197
Traductions .................................................................................................................................... 214
Service Container ............................................................................................................................ 227
Performance ................................................................................................................................... 241
Composants Internes ...................................................................................................................... 244
L'API stable de Symfony2 ................................................................................................................ 253

PDF brought to you by


generated on January 14, 2015

Contents at a Glance | iii

Chapter 1

Les fondamentaux de Symfony2 et HTTP


Flicitations! Grce l'apprentissage de Symfony2, vous tes sur la bonne voie pour devenir un
dveloppeur web plus productif et populaire (en fait la popularit ne dpend que de vous). Symfony2
est construit de manire revenir l'essentiel : implmenter des outils qui vous aident dvelopper
plus rapidement et construire des applications plus robustes, sans pour autant vous gner dans votre
progression. Symfony repose sur les meilleures ides provenant de diverses technologies : les outils et
concepts que vous tes sur le point d'apprendre reprsentent les efforts de milliers de personnes depuis
de nombreuses annes. En d'autres termes, vous n'apprenez pas juste "Symfony", vous apprenez les
fondamentaux du web, les bonnes pratiques de dveloppement, et comment utiliser de nombreuses
nouvelles bibliothques PHP, internes ou indpendantes de Symfony2. Alors, soyez prt !
Fidle la philosophie de Symfony2, ce chapitre dbute par une explication du concept fondamental du
dveloppement web : HTTP. Quelles que soient vos connaissances ou votre langage de programmation
prfr, ce chapitre doit tre lu par tout un chacun.

HTTP est simple


HTTP (HyperText Transfer Protocol pour les geeks) est un langage texte qui permet deux machines
de communiquer ensemble. C'est tout ! Par exemple, lorsque vous regardez la dernire BD de xkcd1, la
conversation suivante (approximative) se droule:

1. http://xkcd.com/

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 4

Et alors que l'actuel langage utilis est un peu plus formel, cela reste toujours trs simple. HTTP est le
terme utilis pour dcrire ce simple langage texte. Et peu importe comment vous dveloppez sur le web, le
but de votre serveur est toujours de comprendre de simples requtes composes de texte, et de retourner
de simples rponses composes elles aussi de texte.
Symfony2 est construit sur les bases de cette ralit. Que vous le ralisiez ou non, HTTP est quelque
chose que vous utilisez tous les jours. Avec Symfony2, vous allez apprendre comment le matriser.

tape 1: Le Client envoie une Requte


Chaque conversation sur le web dbute avec une requte. La requte est un message textuel cr par un
client (par exemple: un navigateur, une application smartphone, etc...) dans un format spcial connu
sous le nom d'HTTP. Le client envoie cette requte un serveur, et puis attend la rponse.
Jetez un oeil la premire partie de l'interaction (la requte) entre un navigateur et le serveur web xkcd:

Dans le langage HTTP, cette requte HTTP ressemblerait quelque chose comme a:
Listing 1-1

1
2
3
4

GET / HTTP/1.1
Host: xkcd.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh)

Ce simple message communique tout ce qui est ncessaire concernant la ressource que le client a
demande. La premire ligne d'une requte HTTP est la plus importante et contient deux choses: l'URI
et la mthode HTTP.
L'URI (par exemple: /, /contact, etc...) est l'adresse unique ou la localisation qui identifie la ressource
que le client veut. La mthode HTTP (par exemple: GET) dfinit ce que vous voulez faire avec la ressource.
PDF brought to you by
generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 5

Les mthodes HTTP sont les verbes de la requte et dfinissent les divers moyens par lesquels vous
pouvez agir sur la ressource:
GET

Rcupre la ressource depuis le serveur

POST

Cre une ressource sur le serveur

PUT

Met jour la ressource sur le serveur

DELETE Supprime la ressource sur le serveur


Avec ceci en mmoire, vous pouvez imaginer ce quoi ressemblerait une requte HTTP pour supprimer
une entre spcifique d'un blog, par exemple:
Listing 1-2

1 DELETE /blog/15 HTTP/1.1

Il y a en fait neuf mthodes HTTP dfinies par la spcification HTTP, mais beaucoup d'entre elles
ne sont pas largement utilises ou supportes. En ralit, beaucoup de navigateurs modernes ne
supportent pas les mthodes PUT et DELETE.

En plus de la premire ligne, une requte HTTP contient invariablement d'autres lignes d'informations
appeles enttes de requte. Les enttes peuvent fournir un large ventail d'informations telles que l'entte
Host, le format de rponse que le client accepte (Accept) et l'application que le client utilise pour
effectuer la requte (User-Agent). Beaucoup d'autres enttes existent et peuvent tre trouvs sur la page
Wikipedia List of HTTP header fields2 (anglais).

tape 2: Le Serveur retourne une rponse


Une fois que le serveur a reu la requte, il connat exactement quelle ressource le client a besoin (via
l'URI) et ce que le client souhaite faire avec cette ressource (via la mthode). Par exemple, dans le cas
d'une requte GET, le serveur prpare la ressource et la retourne dans une rponse HTTP. Considrez la
rponse du serveur web xkcd :

Traduit en HTTP, la rponse envoye au navigateur va ressembler quelque chose comme a :


Listing 1-3

1 HTTP/1.1 200 OK
2 Date: Sat, 02 Apr 2011 21:05:05 GMT
3 Server: lighttpd/1.4.19

2. http://en.wikipedia.org/wiki/List_of_HTTP_header_fields

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 6

4 Content-Type: text/html
5
6 <html>
7
<!-- ... code HTML de la BD xkcd -->
8 </html>

La rponse HTTP contient la ressource demande (le contenu HTML dans ce cas), ainsi que d'autres
informations propos de la rponse. La premire ligne est spcialement importante et contient le code
de statut de la rponse HTTP (200 dans ce cas). Le code de statut communique le rsultat global de la
requte retourne au client. A-t-elle russi ? Y'a-t-il eu une erreur ? Diffrents codes de statut existent qui
indiquent le succs, une erreur, ou que le client a besoin de faire quelque chose (par exemple: rediriger
sur une autre page). Une liste complte peut tre trouve sur la page Wikipedia Liste des codes HTTP3 .
Comme la requte, une rponse HTTP contient de l'information additionnelle sous forme d'enttes
HTTP. Par exemple, Content-Type est un entte de rponse HTTP trs important. Le corps d'une mme
ressource peut tre retourne dans de multiples formats incluant HTML, XML ou JSON et l'entte
Content-Type utilise les Internet Media Types, comme text/html, pour dire au client quel format doit
tre retourn. Une liste des mdias types les plus communs peut tre trouve sur la page Wikipedia Liste
de media type usuels4.
De nombreuses autres enttes existent, dont quelques-uns sont trs puissants. Par exemple, certains
enttes peuvent tre utiliss pour crer un puissant systme de cache.

Requtes, Rponses et Dveloppement Web


Cette conversation requte-rponse est le procd fondamental qui rgit toute communication sur le web.
Et tout aussi important et puissant que ce procd soit, il est inluctablement simple.
Le plus important est que : quel que soit le langage que vous utilisez, le type d'application que vous
construisez (web, mobile, API JSON), ou la philosophie de dveloppement que vous suivez, l'objectif
final d'une application est toujours de comprendre chaque requte et de crer et retourner la rponse
approprie.
Symfony est conu pour correspondre cette ralit.
Pour en savoir plus propos de la spcification HTTP, lisez la RFC originale HTTP 1.1 RFC5 ou
le HTTP Bis6, qui est un effort actif pour clarifier la spcification originale. Un super outil pour
inspecter/vrifier les enttes de la requte et de la rponse durant votre navigation est l'extension
pour Firefox Live HTTP Headers7.

Requtes et rponses en PHP


Alors comment interagissez-vous avec la requte et crez-vous la rponse quand vous utilisez PHP ?
En ralit, PHP vous abstrait une partie du processus global:
Listing 1-4

1 $uri = $_SERVER['REQUEST_URI'];
2 $foo = $_GET['foo'];
3

3. http://fr.wikipedia.org/wiki/Liste_des_codes_HTTP
4.
5.
6.
7.

http://fr.wikipedia.org/wiki/Type_MIME#Liste_de_media_type_usuels
http://www.w3.org/Protocols/rfc2616/rfc2616.html
http://datatracker.ietf.org/wg/httpbis/
https://addons.mozilla.org/fr/firefox/addon/live-http-headers/

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 7

4 header('Content-type: text/html');
5 echo 'L\'URI demande est: '.$uri;
6 echo 'La valeur du paramtre "foo" est: '.$foo;

Aussi trange que cela puisse paratre, cette petite application utilise les informations de la requte HTTP
afin de crer une rponse. Plutt que d'analyser le message texte de la requte HTTP directement, PHP
prpare des variables superglobales telles que $_SERVER et $_GET qui contiennent toute les informations
de la requte. De mme, au lieu de retourner la rponse texte HTTP formate, vous pouvez utiliser la
fonction header() pour crer des enttes de rponse et simplement dlivrer le contenu actuel qui sera la
partie contenu du message de la rponse. PHP va ainsi crer une vritable rponse HTTP et la retourner
au client :
Listing 1-5

1
2
3
4
5
6
7

HTTP/1.1 200 OK
Date: Sat, 03 Apr 2011 02:14:33 GMT
Server: Apache/2.2.17 (Unix)
Content-Type: text/html
L'URI demande est: /testing?foo=symfony
La valeur du paramtre "foo" est: symfony

Requtes et Rponses dans Symfony


Symfony fournit une alternative l'approche brute de PHP via deux classes qui vous permettent
d'interagir avec la requte et la rponse HTTP de manire plus facile. La classe Request8 est une simple
reprsentation oriente objet du message de la requte HTTP. Avec elle, vous avez toute l'information de
la requte votre porte:
Listing 1-6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();

// l'URI demande (par exemple: /about) sans aucun paramtre


$request->getPathInfo();
// obtenir respectivement des variables GET et POST
$request->query->get('foo');
$request->request->get('bar', 'valeur par dfaut si bar est inexistant');
// obtenir les variables SERVER
$request->server->get('HTTP_HOST');
// obtenir une instance de UploadedFile identifie par foo
$request->files->get('foo');
// obtenir la valeur d'un COOKIE value
$request->cookies->get('PHPSESSID');
// obtenir un entte de requte HTTP request header, normalis en minuscules
$request->headers->get('host');
$request->headers->get('content_type');

8. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Request.html

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 8

25 $request->getMethod();
26 $request->getLanguages();

// GET, POST, PUT, DELETE, HEAD


// un tableau des langues que le client accepte

En bonus, la classe Request effectue beaucoup de travail en arrire-plan dont vous n'aurez jamais vous
soucier. Par exemple, la mthode isSecure() vrifie les trois valeurs PHP qui peuvent indiquer si oui ou
non l'utilisateur est connect via une connexion scurise (c-a-d https).

Attributs de ParameterBags et Request


Comme vu ci-dessus, les variables $_GET et $_POST sont accessibles respectivement via les
proprits publiques query et request. Chacun de ces objets est un objet ParameterBag9 qui a des
mthodes comme get()10, has()11, all()12 et bien d'autres. En fait, chaque proprit publique
utilise dans l'exemple prcdent est une instance de ParameterBag.
La classe Request a aussi une proprit publique attributes qui contient des donnes spciales
lies au fonctionnement interne de l'application. Pour le framework Symfony2, la proprit
attributes contient les valeurs retournes par la route identifie, comme _controller, id (si
vous utilisez le joker {id}), et mme le nom de la route (_route). La proprit attributes existe
pour vous permettre d'y stocker des informations spcifiques lies au contexte de la requte.

Symfony fournit aussi une classe Response : une simple reprsentation PHP du message d'une rponse
HTTP. Cela permet votre application d'utiliser une interface oriente objet pour construire la rponse
qui doit tre retourne au client:
Listing 1-7

1
2
3
4
5
6
7
8
9

use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent('<html><body><h1>Hello world!</h1></body></html>');
$response->setStatusCode(200);
$response->headers->set('Content-Type', 'text/html');

// affiche les enttes HTTP suivies du contenu


$response->send();

Si Symfony n'offrait rien d'autre, vous devriez nanmoins dj avoir en votre possession une bote
outils pour accder facilement aux informations de la requte et une interface oriente objet pour crer
la rponse. Bien que vous appreniez les nombreuses et puissantes fonctions de Symfony, gardez l'esprit
que le but de votre application est toujours d'interprter une requte et de crer la rponse approprie base
sur votre logique applicative.
Les classes Request et Response font partie d'un composant autonome inclus dans Symfony
appel HttpFoundation. Ce composant peut tre utilis de manire entirement indpendante de
Symfony et fournit aussi des classes pour grer les sessions et les uploads de fichier.

9.
10.
11.
12.

http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/ParameterBag.html
http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/ParameterBag.html#get()
http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/ParameterBag.html#has()
http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/ParameterBag.html#all()

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 9

Le Parcours de la Requte la Rponse


Comme HTTP lui-mme, les objets Request et Response sont assez simples. La partie difficile de la
cration d'une application est d'crire ce qui vient entre les deux. En d'autres termes, le rel travail
commence lors de l'criture du code qui interprte l'information de la requte et cre la rponse.
Votre application fait probablement beaucoup de choses comme envoyer des emails, grer des
soumissions de formulaires, enregistrer des choses dans une base de donnes, dlivrer des pages HTML
et protger du contenu de faon scurise. Comment pouvez-vous vous occuper de tout cela tout en
conservant votre code organis et maintenable ?
Symfony a t cr pour rsoudre ces problmatiques afin que vous n'ayez pas le faire vous-mme.

Le Contrleur Frontal
Traditionnellement, les applications taient construites de telle sorte que chaque page d'un site avait
son propre fichier physique:
Listing 1-8

1 index.php
2 contact.php
3 blog.php

Il y a plusieurs problmes avec cette approche, ce qui inclut la non-flexibilit des URLs (que se passaitil si vous souhaitiez changer blog.php en news.php sans que tous vos liens existants ne cessent de
fonctionner ?) et le fait que chaque fichier doive manuellement inclure tout un ensemble de fichiers coeurs
pour que la scurit, les connexions la base de donnes et le look du site puissent rester cohrents.
Une bien meilleure solution est d'utiliser un simple fichier PHP appel contrleur frontal: qui s'occupe de
chaque requte arrivant dans votre application. Par exemple:
/index.php

excute index.php

/index.php/contact excute index.php


/index.php/blog

excute index.php

En utilisant la fonction mod_rewrite d'Apache (ou son quivalent avec d'autres serveurs web), les
URLs peuvent tre facilement rcrites afin de devenir simplement /, /contact et /blog.

Maintenant, chaque requte est gre exactement de la mme faon. Plutt que d'avoir des URLs
individuelles excutant des fichiers PHP diffrents, le contrleur frontal est toujours excut, et le routage
( routing ) des diffrentes URLs vers diffrentes parties de votre application est effectu en interne. Cela
rsoud les deux problmes de l'approche originale. Presque toutes les applications web modernes font a
- incluant les applications comme WordPress.

Rester Organis
Dans votre contrleur frontal, vous devez dterminer quelle portion de code doit tre excut et quel est
le contenu qui doit tre retourn. Pour le savoir, vous allez devoir inspecter l'URI entrante et excuter les
diffrentes parties de votre code selon cette valeur. Cela peut rapidement devenir moche:
Listing 1-9

1 // index.php
2 use Symfony\Component\HttpFoundation\Request;
3 use Symfony\Component\HttpFoundation\Response;

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 10

4
5
6
7
8
9
10
11
12
13
14

$request = Request::createFromGlobals();
$path = $request->getPathInfo(); // Le chemin de l'URI demande
if (in_array($path, array('', '/'))) {
$response = new Response('Bienvenue sur le site.');
} elseif ($path == '/contact') {
$response = new Response('Contactez nous');
} else {
$response = new Response('Page non trouve.', 404);
}
$response->send();

Rsoudre ce problme peut tre difficile. Heureusement, c'est exactement ce pourquoi Symfony a t
conu.

Le Droulement d'une Application Symfony


Quand vous laissez Symfony grer chaque requte, la vie est beaucoup plus facile. Symfony suit un
schma simple et identique pour toutes les requtes :

Les requtes entrantes sont interprtes par le routing et passes aux fonctions des contrleurs qui
retournent des objets Response.
Chaque page de votre site est dfinie dans un fichier de configuration du routing qui relie diffrentes
URLs diffrentes fonctions PHP. Le travail de chaque fonction PHP, appele contrleur, est de crer
puis retourner un objet Response, construit partir des informations de la requte, l'aide des outils mis
disposition par le framework. En d'autres termes, le contrleur est le lieu o votre code se trouve : c'est
l que vous interprtez la requte et que vous crez une rponse.
C'est si facile ! Revoyons cela :
Chaque requte excute un mme et unique fichier ayant le rle de contrleur frontal;
Le systme de routing dtermine quelle fonction PHP doit tre excute en se basant sur les
informations provenant de la requte et la configuration de routage que vous avez cr;
La fonction PHP correcte est excute, l o votre code cre et retourne l'objet Response
appropri.

Une Requte Symfony en Action


Sans aller trop loin dans les dtails, voyons ce procd en action. Supposons que vous vouliez ajouter
une page /contact votre application Symfony. Premirement, commencez par ajouter une entre pour
/contact dans votre fichier de configuration du routing:

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 11

Listing 1-10

1 # app/config/routing.yml
2 contact:
3
path:
/contact
4
defaults: { _controller: AcmeDemoBundle:Main:contact }

Cet exemple utilise YAML pour dfinir la configuration de routage. Cette dernire peut aussi tre
crite dans d'autres formats comme XML ou PHP.

Lorsque quelqu'un visite la page /contact, il y a correspondance avec cette route, et le contrleur
spcifi est excut. Comme vous l'apprendrez dans le chapitre sur le routage, la chane de caractres
AcmeDemoBundle:Main:contact est une syntaxe raccourcie qui pointe vers une mthode PHP spcifique
contactAction dans la classe appele MainController:
Listing 1-11

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/DemoBundle/Controller/MainController.php
namespace Acme\DemoBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class MainController
{
public function contactAction()
{
return new Response('<h1>Contactez nous!</h1>');
}
}

Dans cet exemple trs simple, le contrleur cre simplement un objet Response13 contenant l'HTML
"<h1>Contactez nous!</h1>". Dans le chapitre Contrleur, vous allez apprendre comment un contrleur
peut retourner des templates, permettant votre code de prsentation (c-a-d du code qui retourne
du HTML) de se trouver dans un fichier de template spar. Cela libre le contrleur et lui permet de
s'occuper seulement des choses complexes : interagir avec la base de donnes, grer les donnes soumises,
ou envoyer des emails.

Symfony2: Construisez votre application, pas vos outils


Vous savez maintenant que le but de toute application est d'interprter chaque requte entrante et
de crer une rponse approprie. Avec le temps, une application grandit et il devient plus difficile de
garder le code organis et maintenable. Invariablement, les mmes tches complexes reviennent encore
et toujours : persister des lments dans la base de donnes, afficher et rutiliser des templates, grer des
soumissions de formulaires, envoyer des emails, valider des entres d'utilisateurs et grer la scurit.
La bonne nouvelle est qu'aucun de ces problmes n'est unique. Symfony fournit un framework rempli
d'outils qui vous permettent de construire votre application, mais pas vos outils. Avec Symfony2, rien ne
vous est impos : vous tes libre d'utiliser le framework Symfony en entier, ou juste une partie de Symfony
indpendamment.

Outils Autonomes: Les Composants Symfony2


Donc qu'est-ce que Symfony2? Premirement, Symfony2 est une collection de plus de vingt bibliothques
indpendantes qui peuvent tre utilises dans n'importe quel projet PHP. Ces bibliothques, appeles les
13. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 12

Composants Symfony2, contiennent des choses utiles en toute situation, quelle que soit la manire dont
votre projet est dvelopp. Pour en nommer quelques-unes :
HttpFoundation - Contient les classes Request et Response, ainsi que d'autres classes pour la
gestion des sessions et des uploads de fichiers;
Routing - Un puissant et rapide systme qui vous permet de lier une URI spcifique (par
exemple: /contact) l'information lui permettant de savoir comment grer cette requte (par
exemple: excute la mthode contactAction());
Form14 - Un framework complet et flexible pour la cration de formulaires et la gestion de la
soumission de ces derniers;
Validator15 Un systme permettant de crer des rgles propos de donnes et de valider ou
non les donnes utilisateurs soumises suivant ces rgles;
ClassLoader Une bibliothque pour le chargement automatique ( autoloading ) qui permet
aux classes PHP d'tre utilises sans avoir besoin d'inclure ( require ) manuellement les
fichiers contenant ces dernires;
Templating Une bote outils pour afficher des templates, grer leur hritage (c-a-d qu'un
template est dcor par un layout) et effectuer d'autres tches communes aux templates;
Security16 - Une puissante bibliothque pour grer tous les types de scurit dans une
application;
Translation17 Un framework pour traduire les chanes de caractres dans votre application.
Chacun de ces composants est dcoupl et peut tre utilis dans n'importe quel projet PHP, que vous
utilisiez le framework Symfony2 ou non. Chaque partie est faite pour tre utilise en cas de besoin et
remplace quand cela est ncessaire.

La Solution Complte: Le Framework Symfony2


Donc finalement, qu'est-ce que le Framework Symfony2 ? Le Framework Symfony2 est une bibliothque
PHP qui accomplit deux tches distinctes :
1. Fournir une slection de composants (les Composants Symfony2) et des bibliothques tierces
(ex Swiftmailer18 pour envoyer des emails);
2. Fournir une configuration et une bibliothque colle qui lie toutes ces pices ensembles.
Le but du framework est d'intgrer plein d'outils indpendants afin de fournir une exprience
substantielle au dveloppeur. Mme le framework lui-mme est un bundle Symfony2 (c-a-d un plugin)
qui peut tre configur ou remplac entirement.
Symfony2 fournit un puissant ensemble d'outils pour dvelopper rapidement des applications web sans
pour autant s'imposer votre application. Les utilisateurs normaux peuvent commencer rapidement
dvelopper en utilisant une distribution Symfony2, ce qui fournit un squelette de projet avec des
paramtres par dfaut. Pour les utilisateurs avancs, le ciel est la seule limite.

14. https://github.com/symfony/Form
15. https://github.com/symfony/Validator
16. https://github.com/symfony/Security
17. https://github.com/symfony/Translation
18. http://swiftmailer.org/

PDF brought to you by


generated on January 14, 2015

Chapter 1: Les fondamentaux de Symfony2 et HTTP | 13

Chapter 2

Symfony2 versus PHP pur


Pourquoi est-ce que Symfony2 est mieux que simplement ouvrir un fichier et crire du PHP pur?
Si vous n'avez jamais utilis un framework PHP, que vous n'tes pas familier avec la philosophie MVC, ou
simplement que vous vous demandez pourquoi il y a tant d'agitation autour de Symfony2, ce chapitre est
pour vous. Au lieu de vous dire que Symfony2 vous permet de dvelopper plus rapidement de meilleures
applications qu'en PHP pur, vous allez le voir de vos propres yeux.
Dans ce chapitre, vous allez crire une application simple en PHP, puis refactoriser le code afin d'en
amliorer l'organisation. Vous voyagerez dans le temps, en comprenant les dcisions qui ont fait voluer
le dveloppement web au cours de ces dernires annes.
D'ici la fin, vous verrez comment Symfony peut vous pargner les tches banales et vous permet de
reprendre le contrle de votre code.

Un simple blog en PHP


Dans ce chapitre, vous btirez une application de blog en utilisant uniquement du code PHP. Pour
commencer, crez une page qui affiche les billets du blog qui ont t sauvegards dans la base de donnes.
crire en pur PHP est rapide et sale :
Listing 2-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
$result = mysql_query('SELECT id, title FROM post', $link);
?>
<!DOCTYPE html>
<html>
<head>
<title>List of Posts</title>
</head>
<body>
<h1>List of Posts</h1>

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 14

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

<ul>
<?php while ($row = mysql_fetch_assoc($result)): ?>
<li>
<a href="/show.php?id=<?php echo $row['id'] ?>">
<?php echo $row['title'] ?>
</a>
</li>
<?php endwhile; ?>
</ul>
</body>
</html>
<?php
mysql_close($link);
?>

C'est rapide crire, rapide excuter et, comme votre application voluera, impossible maintenir. Il y
a plusieurs problmes qui doivent tre rsolus :
aucune gestion d'erreur : Que se passe-t-il si la connexion la base de donnes choue?
mauvaise organisation : si votre application volue, ce fichier deviendra de moins en moins
maintenable. O devrez-vous mettre le code qui traite la soumission d'un formulaire?
Comment pourrez-vous valider les donnes? O devrez-vous mettre le code qui envoie des
courriels?
Difficult de rutiliser du code : puisque tout se trouve dans un seul fichier, il est impossible
de rutiliser des parties de l'application pour d'autres pages du blog.
Un autre problme non mentionn ici est le fait que la base de donnes est lie MySQL. Mme si
le sujet n'est pas couvert ici, Symfony intgre Doctrine1, une bibliothque ddie l'abstraction des
base de donnes et au mapping objet-relationnel.

Retroussons-nous les manches et rsolvons ces problmes ainsi que d'autres.

Isoler la prsentation
Le code sera mieux structur en sparant la logique applicative du code qui s'occupe de la
prsentation HTML :
Listing 2-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<?php
// index.php
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
$result = mysql_query('SELECT id, title FROM post', $link);
$posts = array();
while ($row = mysql_fetch_assoc($result)) {
$posts[] = $row;
}
mysql_close($link);

1. http://www.doctrine-project.org

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 15

15 // inclut le code de la prsentation HTML


16 require 'templates/list.php';

Le code HTML est maintenant dans un fichier spar (templates/list.php), qui est essentiellement un
fichier HTML qui utilise une syntaxe PHP de template :
Listing 2-3

1 <!DOCTYPE html>
2 <html>
3
<head>
4
<title>List of Posts</title>
5
</head>
6
<body>
7
<h1>List of Posts</h1>
8
<ul>
9
<?php foreach ($posts as $post): ?>
10
<li>
11
<a href="/read?id=<?php echo $post['id'] ?>">
12
<?php echo $post['title'] ?>
13
</a>
14
</li>
15
<?php endforeach; ?>
16
</ul>
17
</body>
18 </html>

Par convention, le fichier qui contient la logique applicative (index.php) est appel contrleur . Le
terme contrleur est un mot que vous allez entendre souvent, quel que soit le langage ou le framework
utilis. Il fait simplement rfrence votre code qui traite les entres de l'utilisateur et prpare une
rponse.
Dans notre cas, le contrleur prpare les donnes de la base de donnes et inclut ensuite un template
pour prsenter ces donnes. Comme le contrleur est isol, vous pouvez facilement changer uniquement
le fichier de template si vous dsirez afficher les billets du blog dans un autre format (par exemple
list.json.php pour un format JSON).

Isoler la logique applicative


Pour l'instant, l'application ne contient qu'une seule page. Mais que faire si une deuxime page a besoin
d'utiliser la mme connexion la base de donnes, ou le mme tableau de billets du blog? Refactorisez
le code pour que le comportement principal et les fonctions d'accs aux donnes de l'application soient
isols dans un nouveau fichier appel model.php :
Listing 2-4

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<?php
// model.php
function open_database_connection()
{
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
return $link;
}
function close_database_connection($link)
{
mysql_close($link);
}

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 16

15
16 function get_all_posts()
17 {
18
$link = open_database_connection();
19
20
$result = mysql_query('SELECT id, title FROM post', $link);
21
$posts = array();
22
while ($row = mysql_fetch_assoc($result)) {
23
$posts[] = $row;
24
}
25
close_database_connection($link);
26
27
return $posts;
28 }

Le nom du fichier model.php est utilis, car la logique et l'accs aux donnes de l'application
sont traditionnellement connus sous le nom de couche modle . Dans une application bien
structure, la majorit du code reprsentant la logique mtier devrait se trouver dans le modle
(plutt que dans le contrleur). Et contrairement cet exemple, seulement une portion (ou
aucune) du modle est en fait concerne par l'accs la base de donnes.

Le contrleur (index.php) est maintenant trs simple :


Listing 2-5

1
2
3
4
5
6

<?php
require_once 'model.php';
$posts = get_all_posts();
require 'templates/list.php';

Maintenant, la seule responsabilit du contrleur est de rcuprer les donnes de la couche modle de
l'application (le modle) et d'appeler le template afficher ces donnes. C'est un exemple trs simple du
patron de conception Modle-Vue-Contrleur.

Isoler le layout
ce point-ci, l'application a t refactorise en trois parties distinctes, offrant plusieurs avantages et
l'opportunit de rutiliser pratiquement la totalit du code pour d'autres pages.
La seule partie du code qui ne peut pas tre rutilise est le layout de la page. Corrigez cela en crant un
nouveau fichier layout.php :
Listing 2-6

1
2
3
4
5
6
7
8
9
10

<!-- templates/layout.php -->


<!DOCTYPE html>
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>

Le template (templates/list.php) peut maintenant simplement hriter du layout :

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 17

Listing 2-7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<?php $title = 'List of Posts' ?>


<?php ob_start() ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="/read?id=<?php echo $post['id'] ?>">
<?php echo $post['title'] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>

Vous avez maintenant une mthode qui permet la rutilisation du layout. Malheureusement, pour
accomplir cela, vous tes forc d'utiliser des fonctions PHP (ob_start(), ob_get_clean()) dans le
template. Symfony utilise un composant de Templates qui permet d'accomplir ce rsultat proprement et
facilement. Vous le verrez en action bientt.

Ajout d'une page pour afficher un billet


La page liste a maintenant t refactorise afin que le code soit mieux organis et rutilisable. Pour le
prouver, ajoutez une page qui va afficher un billet identifi par un paramtre de requte id.
Pour commencer, crez une fonction dans le fichier model.php qui rcupre un seul billet du blog en
fonction d'un id pass en paramtre:
Listing 2-8

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// model.php
function get_post_by_id($id)
{
$link = open_database_connection();
$id = intval($id);
$query = 'SELECT date, title, body FROM post WHERE id = '.$id;
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);
close_database_connection($link);
return $row;
}

Puis crez un nouveau fichier appel show.php - le contrleur pour cette nouvelle page :
Listing 2-9

1
2
3
4
5
6

<?php
require_once 'model.php';
$post = get_post_by_id($_GET['id']);
require 'templates/show.php';

Finalement, crez un nouveau fichier de template - templates/show.php - afin d'afficher un billet du blog
:
PDF brought to you by
generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 18

Listing 2-10

1
2
3
4
5
6
7
8
9
10
11
12

<?php $title = $post['title'] ?>


<?php ob_start() ?>
<h1><?php echo $post['title'] ?></h1>
<div class="date"><?php echo $post['date'] ?></div>
<div class="body">
<?php echo $post['body'] ?>
</div>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>

Crer cette nouvelle page est vraiment facile et aucun code n'est dupliqu. Malgr tout, cette page
introduit des problmes persistants qu'un framework peut rsoudre. Par exemple, un paramtre de
requte id manquant ou invalide va gnrer une erreur fatale. Il serait prfrable de gnrer une erreur
404, mais cela ne peut pas vraiment tre fait facilement. Pire, comme vous avez oubli de nettoyer le
paramtre id avec la fonction intval(), votre base de donnes est sujette des attaques d'injection de
SQL.
Un autre problme est que chaque fichier contrleur doit inclure le fichier model.php. Que se passeraitil si chaque contrleur devait soudainement inclure un fichier additionnel ou effectuer une quelconque
tche globale (comme renforcer la scurit)? Dans l'tat actuel, tout nouveau code devra tre ajout
chaque fichier contrleur. Si vous oubliez de modifier un fichier, il serait bon que ce ne soit pas reli la
scurit...

Un contrleur frontal la rescousse


La solution est d'utiliser un contrleur frontal (front controller): un fichier PHP travers lequel chaque
requte sera traite. Avec un contrleur frontal, les URIs de l'application changent un peu, mais elles sont
plus flexibles :
Listing 2-11

1
2
3
4
5
6
7

Sans contrleur frontal


/index.php
=> page de liste des billets (excution de index.php)
/show.php
=> page d'affichage d'un billet (excution de show.php)
avec index.php comme contrleur frontal
/index.php
=> page de liste des billets (excution de index.php)
/index.php/show
=> page d'affichage d'un billet (excution de index.php)

La portion index.php de l'URI peut tre enleve en utilisant les rgles de rcriture d'URI d'Apache
(ou quivalent). Dans ce cas, l'URI de la page de dtail d'un billet serait simplement /show.

En utilisant un contrleur frontal, un seul fichier PHP (index.php dans notre cas) traite chaque requte.
Pour la page de dtail d'un billet, /index.php/show va donc excuter le fichier index.php, qui est
maintenant responsable de router en interne les requtes en fonction de l'URI complte. Comme vous
allez le voir, un contrleur frontal est un outil trs puissant.

Crer le contrleur frontal


Vous tes sur le point de franchir une tape importante de l'application. Avec un seul fichier qui
traite toutes les requtes, vous pouvez centraliser des fonctionnalits comme la gestion de la scurit,
PDF brought to you by
generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 19

le chargement de la configuration, et le routage. Dans cette application, index.php doit tre assez
intelligent pour afficher la page de liste des billets ou la page de dtail d'un billet en fonction de l'URI
demande :
Listing 2-12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<?php
// index.php

// charge et initialise les bibliothques globales


require_once 'model.php';
require_once 'controllers.php';
// route la requte en interne
$uri = $_SERVER['REQUEST_URI'];
if ('/index.php' == $uri) {
list_action();
} elseif ('/index.php/show' == $uri && isset($_GET['id'])) {
show_action($_GET['id']);
} else {
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}

Pour des raisons d'organisation, les contrleurs (initialement index.php et show.php) sont maintenant
des fonctions PHP et ont t placs dans le fichier controllers.php :
Listing 2-13

1
2
3
4
5
6
7
8
9
10
11

function list_action()
{
$posts = get_all_posts();
require 'templates/list.php';
}
function show_action($id)
{
$post = get_post_by_id($id);
require 'templates/show.php';
}

En tant que contrleur frontal, index.php assume un nouveau rle, celui d'inclure les bibliothques
principales et de router l'application pour que l'un des deux contrleurs (les fonctions list_action() et
show_action()) soit appel. En ralit, le contrleur frontal commence ressembler et agir comme le
mcanisme de Symfony2 qui prend en charge et achemine les requtes.
Un autre avantage du contrleur frontal est d'avoir des URIs flexibles. Veuillez noter que l'URL de
la page d'affichage d'un billet peut tre change de /show /read en changeant le code un seul
endroit. Sans le contrleur frontal, il aurait fallu renommer un fichier. Avec Symfony2, les URLs
sont encore plus flexibles.

Jusqu'ici, l'application est passe d'un seul fichier PHP une organisation qui permet la rutilisation du
code. Vous devriez tre plus heureux, mais loin d'tre satisfait. Par exemple, le systme de routing est
inconstant, et ne reconnatrait pas que la page de liste de billets (/index.php) devrait aussi tre accessible
via / (si Apache rewrite est activ). Aussi, au lieu de dvelopper une application de blog, beaucoup de
temps a t consacr l' architecture du code (par exemple le routing, l'appel aux contrleurs, aux
templates...). Plus de temps devrait tre consacr la prise en charge des formulaires, la validation des
champs, la journalisation et la scurit. Pourquoi rinventer des solutions pour tout ces problmes ?

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 20

Ajoutez une touche de Symfony2


Symfony2 la rescousse. Avant d'utiliser Symfony2, vous devez d'abord le tlcharger. Vous pouvez
le faire en utilisant Composer, qui se chargera de tlcharger la bonne version ainsi que toutes ses
dpendances, et qui chargera galement les classes de manire automatique (grce un autoloader).
Un autoloader est un outil qui rend possible l'utilisation de classes sans avoir inclure les fichiers qui
contiennent chaque classe.
Dans votre rpertoire racine, crez un fichier composer.json avec le contenu suivant :
Listing 2-14

1 {
2
3
4
5
6
7
8 }

"require": {
"symfony/symfony": "2.3.*"
},
"autoload": {
"files": ["model.php","controllers.php"]
}

Ensuite, tlchargez Composer2 puis excutez la commande suivante, qui tlchargera Symfony dans un
rpertoire /vendor :
Listing 2-15

1 $ php composer.phar install

En plus de tlcharger vos dpendances, Composer gnre un fichier vendor/autoload.php, qui prendra
en charge l'autoloading pour tous les fichiers du framework Symfony, de mme que les fichiers spcifis
dans la section autoload de votre composer.json.
La philosophie de base de Symfony est que la principale activit d'une application est d'interprter chaque
requte et de retourner une rponse. Pour cela, Symfony2 fournit les classes Request3 et Response4.
Ces classes sont des reprsentations orientes-objet des requtes HTTP brutes qui sont en train d'tre
excutes et d'une rponse HTTP qui est retourne. Utilisez-les pour amliorer le blog :
Listing 2-16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<?php
// index.php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$uri = $request->getPathInfo();
if ('/' == $uri) {
$response = list_action();
} elseif ('/show' == $uri && $request->query->has('id')) {
$response = show_action($request->query->get('id'));
} else {
$html = '<html><body><h1>Page Not Found</h1></body></html>';
$response = new Response($html, 404);
}

// affiche les enttes et envoie la rponse


$response->send();

2. http://getcomposer.org/download/
3. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Request.html
4. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 21

Les contrleurs sont maintenant chargs de retourner un objet Response. Pour simplifier les choses, vous
pouvez ajouter une nouvelle fonction render_template(), qui agit un peu comme le moteur de template
de Symfony2 :
Listing 2-17

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

// controllers.php
use Symfony\Component\HttpFoundation\Response;
function list_action()
{
$posts = get_all_posts();
$html = render_template('templates/list.php', array('posts' => $posts));
return new Response($html);
}
function show_action($id)
{
$post = get_post_by_id($id);
$html = render_template('templates/show.php', array('post' => $post));
return new Response($html);
}

// fonction helper pour afficher un template


function render_template($path, array $args)
{
extract($args);
ob_start();
require $path;
$html = ob_get_clean();
return $html;
}

En intgrant une petite partie de Symfony2, l'application est plus flexible et fiable. La requte (Request)
permet d'accder aux informations d'une requte HTTP. De manire plus spcifique, la mthode
getPathInfo() retourne une URI pure (retourne toujours /show et jamais /index.php/show). Donc,
mme si l'utilisateur va /index.php/show, l'application est assez intelligente pour router la requte vers
show_action().
L'objet Response offre de la flexibilit pour construire une rponse HTTP, permettant d'ajouter des
enttes HTTP et du contenu au travers d'une interface oriente objet. Et mme si les rponses de cette
application sont simples, cette flexibilit sera un atout lorsque l'application voluera.

L'application exemple en Symfony2


Le blog a beaucoup volu, mais il contient beaucoup de code pour une si simple application. Durant
cette volution, vous avez cr un mcanisme simple de routage et une mthode utilisant ob_start() et
ob_get_clean() pour afficher les templates. Si, pour une raison, vous deviez continuer construire ce
framework , vous pourriez utiliser les composants indpendants Routing5 et Templating6 de Symfony,
qui apportent dj une solution ces problmes.
Au lieu de rsoudre nouveau des problmes classiques, vous pouvez laisser Symfony2 s'en occuper pour
vous. Voici la mme application, en utilisant cette fois-ci Symfony2:
Listing 2-18

5. https://github.com/symfony/Routing
6. https://github.com/symfony/Templating

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 22

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

// src/Acme/BlogBundle/Controller/BlogController.php
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller
{
public function listAction()
{
$posts = $this->get('doctrine')->getManager()
->createQuery('SELECT p FROM AcmeBlogBundle:Post p')
->execute();
return $this->render(
'AcmeBlogBundle:Blog:list.html.php',
array('posts' => $posts)
);
}
public function showAction($id)
{
$post = $this->get('doctrine')
->getManager()
->getRepository('AcmeBlogBundle:Post')
->find($id)
;
if (!$post) {
// cause the 404 page not found to be displayed
throw $this->createNotFoundException();
}
return $this->render(
'AcmeBlogBundle:Blog:show.html.php',
array('post' => $post)
);
}
}

Les deux contrleurs sont toujours simples. Chacun utilise la librairie ORM Doctrine pour rcuprer les
objets de la base de donnes et le composant de Templating pour afficher les templates et retourner un
objet Response. Le template qui affiche la liste est maintenant un peu plus simple :
Listing 2-19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<!-- src/Acme/BlogBundle/Resources/views/Blog/list.html.php -->


<?php $view->extend('::layout.html.php') ?>
<?php $view['slots']->set('title', 'List of Posts') ?>
<h1>List of Posts</h1>
<ul>
<?php foreach ($posts as $post): ?>
<li>
<a href="<?php echo $view['router']->generate(
'blog_show',
array('id' => $post->getId())
) ?>">
<?php echo $post->getTitle() ?>
</a>

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 23

16
</li>
17
<?php endforeach; ?>
18 </ul>

Le layout est peu prs identique :


Listing 2-20

1
2
3
4
5
6
7
8
9
10
11
12
13

<!-- app/Resources/views/layout.html.php -->


<!DOCTYPE html>
<html>
<head>
<title><?php echo $view['slots']->output(
'title',
'Default title'
) ?></title>
</head>
<body>
<?php echo $view['slots']->output('_content') ?>
</body>
</html>

Le template d'affichage d'un billet est laiss comme exercice, cela devrait tre assez simple si vous
vous basez sur le template de liste.

Lorsque le moteur de Symfony2 (appel Kernel) dmarre, il a besoin d'une table qui dfinit quels
contrleurs excuter en fonction des informations de la requte. Une table de routage fournit cette
information dans un format lisible :
Listing 2-21

1 # app/config/routing.yml
2 blog_list:
3
path:
/blog
4
defaults: { _controller: AcmeBlogBundle:Blog:list }
5
6 blog_show:
7
path:
/blog/show/{id}
8
defaults: { _controller: AcmeBlogBundle:Blog:show }

Maintenant que Symfony2 prend en charge toutes les taches banales, le contrleur frontal est
extrmement simple. Et comme il fait trs peu de chose, vous n'aurez jamais le modifier une fois que
vous l'aurez cr (et si vous utilisez une distribution de Symfony2, vous n'aurez mme pas le crer !):
Listing 2-22

1
2
3
4
5
6
7
8

// web/app.php
require_once __DIR__.'/../app/bootstrap.php';
require_once __DIR__.'/../app/AppKernel.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->handle(Request::createFromGlobals())->send();

Le seul rle du contrleur frontal est d'initialiser le moteur Symfony2 (Kernel) et de lui passer un objet
Request traiter. Le coeur de Symfony2 utilise alors la table de routage pour dterminer quel contrleur
appeler. Comme prcdemment, c'est la mthode du contrleur de retourner un objet Response.

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 24

Pour une reprsentation visuelle qui montre comment Symfony2 traite chaque requte, voir le diagramme
de flux de contrle d'une requte.

En quoi Symfony2 tient ses promesses


Dans les chapitres suivants, vous en apprendrez plus sur le fonctionnement chaque lment de Symfony
et sur la structure recommande d'un projet. Pour l'instant, voyons en quoi la migration du blog depuis
une version PHP en une version Symfony2 nous simplifie la vie :
Votre application est constitue de code clair et bien organis (mme si Symfony ne vous
force pas le faire). Cela encourage la rutilisabilit et permet aux nouveaux dveloppeurs
d'tre productifs sur votre projet plus rapidement;
100% du code que vous crivez est pour votre application. Vous n'avez pas dvelopper
ou maintenir des outils de bas niveau tel que le chargeur automatique, le routage, ou les
contrleurs;
Symfony2 vous donne accs des outils open source comme Doctrine et le composants de
templates, de scurit, de formulaires, de validation et de traduction (pour n'en nommer que
quelques-uns);
L'application profite maintenant d'URLs compltement flexibles, grce au composant de
routage (Routing);
L'architecture centre autour du protocole HTTP vous donne accs des outils puissants tels
que le cache HTTP permis par le cache HTTP interne de Symfony2 ou par d'autres outils
plus puissants tels que Varnish7. Ce point est couvert dans un chapitre consacr au cache.
Et peut-tre le point le plus important, en utilisant Symfony, vous avez maintenant accs un ensemble
d'outils de qualit open source dvelopps par la communaut Symfony2 ! Un large choix d'outils
de la communaut Symfony2 se trouve sur KnpBundles.com8.

De meilleurs templates
Si vous choisissez de l'utiliser, Symfony2 vient de facto avec un moteur de template appel Twig9 qui rend
les templates plus rapides crire et plus faciles lire. Cela veut dire que l'application exemple pourrait
contenir moins de code ! Prenez par exemple, le template de liste de billets crit avec Twig :
Listing 2-23

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

{# src/Acme/BlogBundle/Resources/views/Blog/list.html.twig #}
{% extends "::layout.html.twig" %}
{% block title %}List of Posts{% endblock %}
{% block body %}
<h1>List of Posts</h1>
<ul>
{% for post in posts %}
<li>
<a href="{{ path('blog_show', {'id': post.id}) }}">
{{ post.title }}
</a>
</li>
{% endfor %}
</ul>
{% endblock %}

7. https://www.varnish-cache.org
8. http://knpbundles.com/
9. http://twig.sensiolabs.org

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 25

Le template du layout associ layout.html.twig est encore plus simple crire :


Listing 2-24

1
2
3
4
5
6
7
8
9
10

{# app/Resources/views/layout.html.twig #}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Default title{% endblock %}</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>

Twig est trs bien support par Symfony2. Et bien que les templates PHP vont toujours tre supports
par Symfony2, nous allons continuer vanter les nombreux avantages de Twig. Pour plus d'information,
voir le chapitre sur les templates.

Apprenez en lisant le Cookbook


Comment utiliser PHP plutt que Twig dans les templates
Comment dfinir des contrleurs en tant que Services

PDF brought to you by


generated on January 14, 2015

Chapter 2: Symfony2 versus PHP pur | 26

Chapter 3

Installer et Configurer Symfony


Le but de ce chapitre est de vous permettre de dmarrer avec une application construite avec Symfony.
Heureusement, Symfony propose un systme de distributions . Ce sont des projets Symfony
fonctionnels pour dmarrer que vous pouvez tlcharger et qui vous permettent de dvelopper
immdiatement.
Si vous cherchez des instructions sur la meilleure faon de crer un nouveau projet et de le stocker
dans un gestionnaire de versions, lisez Utiliser un Gestionnaire de Versions.

Tlcharger une Distribution Symfony2


Premirement, vrifiez que vous avez install et configur un serveur web (comme Apache) avec
PHP. Pour plus d'informations sur les prrequis Symfony2, lisez le chapitre prrequis.

Les distributions Symfony2 sont des applications entirement fonctionnelles qui incluent les
bibliothques du coeur de Symfony2, une slection de bundles utiles, une arborescence pratique et
une configuration par dfaut. Quand vous tlchargez une distribution Symfony2, vous tlchargez
un squelette d'application qui peut tre immdiatement utilis pour commencer dvelopper votre
application.
Commencez par visiter la page de tlchargement de Symfony2 http://symfony.com/download1. Sur cette
page, vous verrez la Symfony Standard Edition, qui est la principale distribution Symfony2. Il y a deux
faons de dmarrer votre projet :

1. http://symfony.com/download

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 27

Option 1) Composer
Composer2 est une bibliothque de gestion de dpendances pour PHP, que vous pouvez utiliser pour
tlcharger l'dition Standard de Symfony2.
Commencez par tlcharger Composer3 dans un quelconque rpertoire sur votre ordinateur. Si vous avez
curl d'install, cela est aussi facile que ce qui suit :
Listing 3-1

1 curl -s https://getcomposer.org/installer | php

Si votre ordinateur n'est pas prt utiliser Composer, vous allez voir quelques recommandations
lors de l'excution de cette commande. Suivez ces recommandations afin que Composer puisse
fonctionner correctement sur votre machine locale.

Composer est un fichier PHAR excutable, que vous pouvez utiliser pour tlcharger la Distribution
Standard :
Listing 3-2

1 $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/


Symfony 2.3.0

Pour une version exacte, remplacez 2.3.0 par la dernire version de Symfony. Pour plus de dtails,
lisez Installation de Symfony4

Pour tlcharger les vendor plus rapidement, ajoutez l'option "--prefer-dist" la fin de la
commande Composer .

Cette commande peut prendre plusieurs minutes pour s'excuter, car Composer tlcharge la
Distribution Standard ainsi que toutes les bibliothques vendor dont elle a besoin. Lorsque la
commande a termin son excution, vous devriez avoir un rpertoire qui ressemble quelque chose
comme a :
Listing 3-3

1 path/to/webroot/ <- votre rpertoire racine


2
Symfony/ <- le nouveau rpertoire
3
app/
4
cache/
5
config/
6
logs/
7
src/
8
...
9
vendor/
10
...
11
web/
12
app.php
13
...

2. http://getcomposer.org/
3. http://getcomposer.org/download/
4. http://symfony.com/download

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 28

Option 2) Tlcharger une archive


Vous pouvez aussi tlcharger une archive contenant l'dition Standard. Vous devrez alors faire deux
choix :
Tlcharger l'archive au format .tgz ou .zip. Les deux sont quivalentes donc tlchargez
celle avec laquelle vous vous sentez le plus l'aise ;
Tlchargez la distribution avec ou sans vendors . Si vous prvoyez d'utiliser d'autres
bibliothques tierces ou d'autres bundles et de les grer via Composer, vous devriez
probablement tlcharger la distribution sans vendors .
Tlchargez l'une des archives quelque part dans le dossier racine de votre serveur web et extrayezla. Depuis une interface de commande UNIX, cela peut tre fait avec l'une des commandes suivantes
(remplacez ### par le nom du fichier) :
Listing 3-4

1
2
3
4
5

# pour l'archive .tgz


$ tar zxvf Symfony_Standard_Vendors_2.3.###.tgz
# pour l'archive .zip
$ unzip Symfony_Standard_Vendors_2.3.###.zip

Si vous avez tlcharg la distribution sans les vendors , vous devez lire la section suivante.
Vous pouvez facilement surcharger la structure de rpertoires par dfaut. Lisez Comment
surcharger la structure de rpertoires par dfaut de Symfony pour plus d'informations

Tous les fichiers publics et les contrleurs frontaux qui prennent en charge les requtes entrantes d'une
application Symfony2 se trouvent dans le rpertoire Symfony/web/. En consquence, en supposant que
vous avez extrait votre archive la racine de votre serveur web ou de votre virtual host, l'URL de votre
application commencera par http://localhost/Symfony/web/. Pour avoir des URLs plus courtes et
plus agrables, vous devrez faire pointer la racine de votre serveur web vers le rpertoire Symfony/
web/. Bien que ce ne soit pas ncessaire pour dvelopper, c'est recommand pour une application en
production car tous les fichiers systme et lis la configuration seront inaccessibles aux clients. Pour
plus d'informations sur la manire de configurer vos serveurs web, lisez les documentations suivantes :
Apache5 | Nginx6 .
Les exemples suivants supposent que vous n'avez pas touch la configuration de votre racine, donc
toutes les urls commencent par http://localhost/Symfony/web/

Mettre jour les Vendors


A ce stade, vous avez tlcharg un projet Symfony entirement fonctionnel dans lequel vous allez
commencer dvelopper votre propre application. Un projet Symfony dpend d'un certain nombre de
bibliothques externes. Celles-ci sont tlcharges dans le rpertoire vendor/ de votre projet via une
bibliothque appele Composer7.
Suivant la manire dont vous avez tlcharg Symfony, vous pourriez ou non avoir besoin de mettre
jour vos vendors ds maintenant. Mais, mettre jour vos vendors est toujours sr, et vous garantit
d'avoir toutes les bibliothques dont vous avez besoin.
5. http://httpd.apache.org/docs/current/mod/core.html#documentroot
6. http://wiki.nginx.org/Symfony
7. http://getcomposer.org/

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 29

tape 1: Tlchargez Composer8 (Le nouveau systme de package PHP)


Listing 3-5

1 curl -s http://getcomposer.org/installer | php

Assurez-vous d'avoir tlcharg composer.phar dans le mme rpertoire que celui o se situe le fichier
composer.json (par dfaut la racine de votre projet Symfony).
tape 2: Installer les vendors
Listing 3-6

1 $ php composer.phar install

Cette commande tlcharge toutes les bibliothques ncessaires - incluant Symfony elle-mme - dans le
rpertoire vendor/.
Si vous n'avez pas install curl, vous pouvez juste tlcharger le fichier installer manuellement
cette adresse http://getcomposer.org/installer9. Placez ce fichier dans votre projet puis lancez les
commandes :
Listing 3-7

1 php installer
2 php composer.phar install

Lorsque vous excutez php composer.phar install ou php composer.phar update, Composer
va excuter les commandes install/update pour vider le cache et installer les ressources ( assets
en anglais). Par dfaut, les ressources seront copies dans le rpertoire web.
Au lieu de copier les ressources Symfony, vous pouvez crer des liens symboliques si votre systme
d'exploitation les supporte. Pour crer des liens symboliques, ajoutez une entre dans le noeud
extra de votre fichier composer.json avec la cl symfony-assets-install et la valeur symlink :
Listing 3-8

"extra": {
"symfony-app-dir": "app",
"symfony-web-dir": "web",
"symfony-assets-install": "symlink"
}

Si vous passez relative au lieu de symlink la commande symfony-assets-install , cette


dernire gnrera des liens symboliques relatifs.

Configuration et installation
Maintenant, toutes les bibliothques tierces ncessaires sont dans le rpertoire vendor/. Vous avez
galement une application par dfaut installe dans le rpertoire app/ et un exemple de code dans le
rpertoire src/.
Symfony2 est livr avec un testeur de configuration de votre serveur afin de vrifier que votre serveur
web et PHP sont bien configur pour utiliser Symfony. Utilisez l'URL suivante pour vrifier votre
configuration :
Listing 3-9

1 http://localhost/config.php

S'il y a des problmes, corrigez-les maintenant avant de poursuivre.


8. http://getcomposer.org/
9. http://getcomposer.org/installer

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 30

Dfinir les permissions


Un des problmes les plus frquents est que les rpertoires app/cache et app/logs ne sont pas
accessibles en criture par le serveur web et par l'utilisateur de ligne de commande. Sur un systme
UNIX, si votre utilisateur de ligne de commande est diffrent de celui du serveur web, vous pouvez
lancer les commandes suivantes une fois dans votre projet pour vous assurer que les permissions
sont correctement dfinies.
Notez que tous les serveurs web n'utilisent pas l'utilisateur www-data comme dans les
exemples ci-dessous. Veuillez vrifier quel utilisateur votre serveur web utilise et utilisez-le la
place de www-data.
Sur un systme UNIX, vous pouvez le faire grce une des commandes suivantes :
Listing 3-10

1 $ ps aux | grep httpd

ou
Listing 3-11

1 $ ps aux | grep apache

1. Utiliser l'ACL sur un systme qui supporte chmod +a


Beaucoup de systmes autorisent l'usage de la commande chmod +a. Essayez d'abord la premire
mthode, et si vous avez une erreur, essayez la seconde. Assurez-vous de bien remplacer www-data
par l'utilisateur de votre serveur web dans la premire commande chmod:
Listing 3-12

1
2
3
4
5
6

$ rm -rf app/cache/*
$ rm -rf app/logs/*
$ HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v
root | head -1 | cut -d\ -f1`
$ sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit"
app/cache app/logs
$ sudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit"
app/cache app/logs

2. Utiliser l'ACL sur un systme qui ne supporte pas chmod +a


Certains systmes ne supportent pas la commande chmod +a, mais supportent un autre utilitaire
appel setfacl. Vous devrez sans doute activer le support ACL10 sur votre partition et installer
setfacl avant de pouvoir l'utiliser (comme c'est le cas avec Ubuntu), de cette faon :
Listing 3-13

1 $ HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v


2 root | head -1 | cut -d\ -f1`
3 $ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs
$ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs

3. Sans utiliser l'ACL


Si vous n'avez pas les droits de changer les accs aux rpertoires, vous aurez besoin de changer le
umask pour que les rpertoires cache et log soit accessibles en criture au groupe ou aux autres
(cela dpend si l'utilisateur serveur web et l'utilisateur de ligne de commande sont dans le mme
groupe ou non). Pour faire ceci, ajoutez la ligne suivante au dbut des fichiers app/console, web/
app.php et web/app_dev.php:
Listing 3-14

1 umask(0002); // Dfinit une permission 0775


2
3 // ou

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 31

4
5 umask(0000); // Dfinit une permission 0777

Notez qu'utiliser l'ACL est recommand si vous y avez accs sur votre serveur car changer le umask
n'est pas sr.

Lorsque tout est bon, cliquez sur Go to the Welcome page pour afficher votre premire vraie page
Symfony2 :
Listing 3-15

1 http://localhost/app_dev.php/

Symfony2 devrait vous accueillir et vous fliciter pour tout le travail accompli jusqu'ici !

Commencer dvelopper
Maintenant que vous avez une application Symfony2 fonctionnelle, vous pouvez commencer
dvelopper ! Votre distribution devrait contenir un exemple de code. Vrifiez le fichier README.md inclus
avec la distribution (ouvrez-le en tant que fichier texte) pour savoir quel exemple de code est inclus avec
votre distribution et savoir comment le supprimer par la suite.
Si vous dcouvrez Symfony, jetez un oeil au chapitre La cration de pages avec Symfony2 , o vous
apprendrez comment crer des pages, changer la configuration et faire tout ce que vous aurez besoin de
faire dans votre nouvelle application.
Assurez-vous aussi de consulter le Cookbook, qui contient une grande varit d'articles expliquant
comment solutionner des problmes spcifiques avec Symfony.
Si vous voulez supprimer le code d'exemple de votre distribution, jetez un oeil cet article du
Cookbook : "Comment supprimer le AcmeDemoBundle"

10. https://help.ubuntu.com/community/FilePermissionsACLs

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 32

Utiliser un Gestionnaire de Versions


Si vous utilisez un systme de contrle de version comme Git ou Subversion, vous pouvez le configurer
et commencer commiter votre projet normalement. La Symfony Standard edition est le point de dpart
de votre nouveau projet.
Pour des instructions spcifiques sur la meilleure faon de grer votre projet avec git, lisez le chapitre
Comment crer et stocker un projet Symfony2 dans git.

Ignorer le rpertoire vendor/


Si vous avez tlcharg l'archive sans vendors, vous pouvez ignorer tout le rpertoire vendor/ en toute
scurit et ne pas le commiter. Avec Git, cela se fait en crant le fichier .gitignore et en y ajoutant la
ligne suivante:
Listing 3-16

1 /vendor/

Maintenant, le rpertoire vendor ne sera pas commit sur votre systme de gestion de code. C'est plutt
bien (en fait c'est gnial !) car lorsque quelqu'un clone ou rcupre le projet, il lui suffit de lancer la
commande php composer.phar install pour rcuprer toutes les bibliothques ncessaires.

PDF brought to you by


generated on January 14, 2015

Chapter 3: Installer et Configurer Symfony | 33

Chapter 4

La cration de pages avec Symfony2


Crer des pages avec Symfony2 se fait en simplement deux tapes :
Crez une route : Une route dfinit l'URL (ex: /apropos) pour votre page et spcifie un
contrleur (une fonction PHP) que Symfony2 devrait excuter quand l'URL d'une requte
HTTP correspond une route que vous avez dfinie.
Crez un contrleur : Un contrleur est une fonction PHP qui traitera la requte HTTP et la
transformera en un objet Response Symfony2 qui sera retourn l'utilisateur.
Cette approche trs simple est excellente, car elle correspond la faon dont fonctionne le Web. Chaque
interaction sur le Web est initie par une requte HTTP. Le but de votre application est simplement
d'interprter cette requte et de lui retourner une Response HTTP approprie.
Symfony2 suit cette philosophie et vous fournit des outils et conventions pour garder votre application
organise tout en pouvant devenir plus complexe et frquente.
Cela vous parait suffisamment simple ? Alors, allons-y !

La page Hello Symfony !


Commenons avec une application traditionnelle Hello World ! . Quand nous aurons termin,
l'utilisateur sera capable de recevoir un message de salutation personnalis (ex Hello Symfony ) en se
rendant l'URL suivante :
Listing 4-1

1 http://localhost/app_dev.php/hello/Symfony

En fait, vous serez capable de pouvoir remplacer Symfony par n'importe quel autre nom qui doit tre
salu. Afin de crer cette page, suivez ce simple processus en deux tapes.

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 34

Ce tutoriel suppose que vous ayez dj tlcharg et configur Symfony2 sur votre serveur web.
L'URL ci-dessus suppose que localhost pointe vers le rpertoire web de votre nouveau projet
Symfony2. Pour des instructions dtailles ce sujet, lisez la documentation du serveur web que
vous utilisez. Voici les documentations de serveur que vous utilisez probablement :

Pour Apache HTTP Server, allez voir la documentation Apache's DirectoryIndex1.


Pour Nginx, allez voir documentation Nginx HttpCoreModule2.

Avant de commencer : Crez un Bundle


Avant de commencer, vous devez crer un bundle. Dans Symfony2, un bundle est comme un plugin,
except le fait que tout le code de votre application sigera dans un bundle.
Un bundle n'est rien d'autre qu'un rpertoire qui contient tout ce qui est relatif une fonctionnalit
spcifique, ce qui inclut les classes PHP, la configuration et mme les feuilles de style et le javascript (voir
Le Systme de Bundles).
Afin de crer un bundle nomm AcmeHelloBundle (un bundle fictif que vous crerez dans ce chapitre),
lancez la commande suivante et suivez les instructions affiches l'cran (choisissez les options par
dfaut) :
Listing 4-2

1 $ php app/console generate:bundle --namespace=Acme/HelloBundle --format=yml

Un rpertoire src/Acme/HelloBundle est cr pour le bundle. Une ligne est aussi automatiquement
ajoute au fichier app/AppKernel.php afin que le bundle soit correctement enregistr:
Listing 4-3

1
2
3
4
5
6
7
8
9
10
11

// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Acme\HelloBundle\AcmeHelloBundle(),
);
// ...
return $bundles;
}

Maintenant que votre bundle est mis en place, vous pouvez commencer construire votre application
l'intrieur du bundle.

Etape 1 : Crez la Route


Dans une application Symfony2, le fichier de configuration des routes se trouve par dfaut dans app/
config/routing.yml. Comme toute configuration dans Symfony2, vous pouvez galement choisir
d'utiliser des fichiers XML ou PHP afin de configurer vos routes.
Si vous regardez le fichier de routage principal, vous verrez que Symfony a dj ajout une entre lorsque
vous avez gnr le AcmeHelloBundle :
1. http://httpd.apache.org/docs/2.0/mod/mod_dir.html
2. http://wiki.nginx.org/HttpCoreModule#location

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 35

Listing 4-4

1 # app/config/routing.yml
2 acme_hello:
3
resource: "@AcmeHelloBundle/Resources/config/routing.yml"
4
prefix:
/

Ce code est trs basique : il dit Symfony de charger la configuration de routage depuis le fichier
Resources/config/routing.yml qui se trouve dans le AcmeHelloBundle. Cela signifie que vous pouvez
placer votre configuration de routage directement dans le fichier app/config/routing.yml ou organiser
vos routes dans votre application et les importer depuis ce fichier.
Maintenant que le fichier routing.yml du bundle est import, ajoutez la nouvelle route qui dfinit l'URL
de la page que vous tes sur le point de crer :
Listing 4-5

1 # src/Acme/HelloBundle/Resources/config/routing.yml
2 hello:
3
pattern: /hello/{name}
4
defaults: { _controller: AcmeHelloBundle:Hello:index }

Le routage est constitu de deux parties principales : le pattern, qui est l'URL correspondante cette
route, et un tableau par dfaut, qui spcifie le contrleur qui devra tre excut. La syntaxe pour
le paramtre dans le pattern ({name}) est un joker. Cela signifie que hello/Ryan, hello/Bernard ou
n'importe quelle URL similaire correspondra cette route. Le paramtre {name} sera galement pass
notre contrleur afin que nous puissions utiliser la valeur afin de saluer l'utilisateur.
Le systme de routage dispose de beaucoup d'autres fonctionnalits qui vous permettront de crer
des structures d'URL puissantes et flexibles dans votre application. Pour plus de dtails, lisez le
chapitre ddi aux routes Routing.

Etape 2 : Crez le Contrleur


Quand une URL comme /hello/Ryan est traite par l'application, la route hello est reconnue et le
contrleur AcmeHelloBundle:Hello/index est excut par le framework. L'tape suivante est de crer ce
contrleur.
Le contrleur - AcmeHelloBundle:Hello:index est le nom logique du contrleur, et il est associ
la mthode indexAction d'une classe PHP appele Acme\HelloBundle\Controller\HelloController.
Commencez par crer ce fichier dans votre AcmeHelloBundle :
Listing 4-6

1
2
3
4
5
6

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
class HelloController
{
}

En ralit, un contrleur n'est rien d'autre qu'une mthode PHP que vous crez et que Symfony excute.
C'est cet endroit que le code propre l'application utilisera les informations de la requte afin de
construire et prparer la ressource demande par la requte. Except dans certaines situations avances,
le rsultat final d'un contrleur sera toujours le mme : un objet Response Symfony2.
Crez la mthode indexAction que Symfony excutera lorsque la route hello sera identifie :
Listing 4-7

1 // src/Acme/HelloBundle/Controller/HelloController.php
2 namespace Acme\HelloBundle\Controller;

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 36

3
4
5
6
7
8
9
10
11
12

use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($name)
{
return new Response('<html><body>Hello '.$name.'!</body></html>');
}
}

Le contrleur est simple : il cre un nouvel objet Response qui a pour premier argument le contenu qui
doit tre utilis dans la rponse (une petite page HTML dans notre cas).
Flicitations ! Aprs avoir n'avoir cr qu'une route et un contrleur, vous avez une page pleinement
fonctionnelle ! Si vous avez tout effectu correctement, votre application devrait vous saluer:
Listing 4-8

1 http://localhost/app_dev.php/hello/Ryan

Vous pouvez aussi voir votre application en environnement de prod en allant l'adresse :
Listing 4-9

1 http://localhost/app.php/hello/Ryan

Si vous avez une erreur, c'est certainement parce que vous avez besoin de vider votre cache grce
la commande :
Listing 4-10

1 $ php app/console cache:clear --env=prod --no-debug

Une troisime tape optionnelle dans ce processus est de crer un template.


Les contrleurs sont le point central de votre code et un lment cl de la cration de pages. Pour
plus d'informations lisez le chapitre Chapitre Contrleurs.

tape 3 facultative : Crez le Template


Les templates vous permettent de dplacer toute la prsentation (ex: code HTML) dans un fichier spar
et de rutiliser diffrentes portions d'un layout. A la place d'crire le code HTML dans le contrleur,
retournez plutt un template :
Listing 4-11

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HelloController extends Controller
{
public function indexAction($name)
{
return $this->render('AcmeHelloBundle:Hello:index.html.twig', array('name' =>
$name));

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 37

13
14
// render a PHP template instead
15
// return $this->render('AcmeHelloBundle:Hello:index.html.php', array('name' =>
16 $name));
}
}

Afin d'utiliser la mthode render()3, votre contrleur doit tendre la classe


Symfony\Bundle\FrameworkBundle\Controller\Controller (API docs: Controller4), qui
ajoute des raccourcis pour des tches frquemment utilises dans les contrleurs. Dans l'exemple
ci-dessus, c'est ce qui est fait en ajoutant la ligne use et en tendant la classe Controller la ligne
6.

La mthode render() cre un objet Response qui contient le contenu d'un template rendu. Comme tout
autre contrleur, vous retournerez cet objet Response.
Notez qu'il y a deux diffrents exemples afin de rendre un template. Par dfaut, Symfony2 supporte deux
langages diffrents de templates : les templates classiques PHP et les simples, mais puissants templates
Twig. Ne paniquez pas, vous tes libres de choisir celui que vous dsirez voire les deux.
Le contrleur rend le template AcmeHelloBundle:Hello:index.html.twig, qui suit la convention de
nommage :
NomBundle:NomContrleur:NomTemplate
C'est le nom logique du template, qui est associ une location physique selon la convention suivante.
/chemin/vers/NomBundle/Resources/views/NomContrleur/NomTemplate
Dans ce cas, AcmeHelloBundle est le nom de bundle, Hello est le nom du contrleur et enfin
index.html.twig est le template :
Listing 4-12

1
2
3
4
5
6

{# src/Acme/HelloBundle/Resources/views/Hello/index.html.twig #}
{% extends '::base.html.twig' %}
{% block body %}
Hello {{ name }}!
{% endblock %}

Analysons maintenant le template Twig ligne par ligne :


Ligne 2 : Le symbole extends dfinit un template parent. Le template dfinit explicitement un
fichier layout dans lequel il sera insr.
Ligne 4 : Le symbole block indique que tout ce qui est l'intrieur doit tre plac l'intrieur
d'un bloc appel body. Comme vous le voyez, c'est en dfinitive la responsabilit du template
parent (base.html.twig) de rendre le bloc body.

3. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#render()
4. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/Controller/Controller.html

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 38

Le nom de fichier du template parent, ::base.html.twig, est dispens des portions NomBundle et
NomContrleur (remarquez les deux points (::) au dbut). Ceci signifie que le template se situe en
dehors du bundle et dans le rpertoire app.
Listing 4-13

1
2
3
4
5
6
7
8
9
10
11
12
13
14

{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
<link rel="shortcut icon" href="{{ asset('favicon.ico') }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>

Le fichier du template de base dfinit le layout HTML en rend le bloc body que vous avez dfini dans le
template index.html.twig. Il rend galement un bloc title, que vous pouvez choisir de dfinir dans le
template index.html.twig. Si vous ne dfinissez pas le bloc title dans le template enfant, il aura pour
valeur par dfaut Welcome!.
Les templates sont une faon puissante de rendre et d'organiser le contenu de votre page. Les templates
peuvent tout rendre, des layouts HTML aux codes CSS, ou n'importe quoi d'autre que le contrleur peut
avoir besoin de retourner l'utilisateur.
Dans le cycle de vie d'une requte, le template est un outil facultatif. Souvenez vous que le but de
chaque contrleur est de renvoyer un objet Response. Le moteur de templates est un outil puissant, bien
qu'optionnel, pour crer le contenu de cet objet Response.

La structure des rpertoires


Aprs seulement quelques courtes sections, vous comprenez dj la philosophie derrire la cration et
le rendu de pages dans Symfony2. Vous avez dj commenc voir comment les projets Symfony2 sont
structurs et organiss. A la fin de cette section, vous saurez o trouver et o mettre les diffrents types
de fichiers et pourquoi.
Bien qu'entirement flexible, chaque application a par dfaut la mme structure de rpertoires basique et
recommande :

app/: Ce rpertoire contient la configuration de l'application;


src/: Tout le code PHP du projet est stock ici;
vendor/: Par convention, toutes les bibliothques tierces (additionnelles) sont places ici;
web/: Le rpertoire racine web qui contient tous les fichiers publiquement accessibles;

Le rpertoire Web
Le rpertoire web contient tous les fichiers publics et statiques incluant les images, les feuilles de style et
les javascripts. Il contient galement le contrleur frontal ( front controller en anglais) :
Listing 4-14

1 // web/app.php
2 require_once __DIR__.'/../app/bootstrap.php.cache';
3 require_once __DIR__.'/../app/AppKernel.php';
4

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 39

5
6
7
8
9

use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();

Le contrleur frontal (app.php dans cet exemple) est le fichier excut lorsque l'on appelle une
application Symfony2. Son rle est d'utiliser une classe Kernel, AppKernel, pour initialiser l'application
(bootstrap).
Avoir un contrleur frontal signifie des URL diffrentes et plus flexibles que dans une application
en pur php. Lorsqu'on utilise un contrleur frontal, les URLs sont formates comme suit :

Listing 4-15

1 http://localhost/app.php/hello/Ryan

Le contrleur frontal, app.php, est excut et l'URL interne: /hello/Ryan est traite par
l'application en se basant sur la configuration du routage. En utilisant les rgles du module
Apache mod_rewrite, vous pouvez forcer le script app.php tre excut sans avoir besoin de le
mentionner dans l'URL:
Listing 4-16

1 .. code-block:: text
2
3 http://localhost/hello/Ryan

Les contrleurs frontaux sont essentiels pour traiter chaque requte. Cependant, vous aurez rarement
besoin de les modifier ou mme d'y penser. Ils seront abords dans la section Environnements .

Le rpertoire de l'application (app)


Comme nous l'avons vu dans le contrleur frontal, la classe AppKernel est le point d'entre principal de
l'application et est charge de toute la configuration. De ce fait, elle est stocke dans le rpertoire app/.
Cette classe doit implmenter deux mthodes qui dfinissent tout ce dont Symfony a besoin de savoir
propos de votre application. Vous n'avez pas vous soucier de ces mthodes en commenant - Symfony
les complte pour vous avec des valeurs par dfaut.
registerBundles(): renvoit un tableau de tous les bundles dont l'application a besoin pour
fonctionner (voir le Le Systme de Bundles) ;
registerContainerConfiguration()
registerContainerConfiguration(): Charge le fichier de configuration ressource
principal de l'application (voir la section Configuration de l'Application).
Dans le dveloppement au quotidien, vous utiliserez principalement le rpertoire app/ pour modifier
les fichiers de configuration et de routage dans le rpertoire app/config (voir Configuration de
l'Application). app/ contient galement le rpertoire de cache de l'application (app/cache), le rpertoire
des logs (app/logs) et un rpertoire pour les ressources communes l'application entire (app/
Resources). Nous en apprendrons plus sur ces rpertoires dans de prochains chapitres.

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 40

Autoloading
Lorsque Symfony se charge, un fichier spcial - app/autoload.php - est inclus. Ce fichier s'occupe
de configurer l'autoloader qui chargera automatiquement tous vos fichiers depuis le rpertoire
src/ et toutes les bibliothques tierces depuis le repertoire vendor/.
Grce l'autoloader, vous n'avez jamais vous soucier d'utiliser les instructions include ou
require. Symfony2 se base sur l'espace de nom (namespace) d'une classe pour dterminer son
emplacement et l'inclure automatiquement le fichier votre place l'instant o vous en avez
besoin.
L'autoloader est dj configur pour regarder dans le dossier src/ pour chacune de vos classes
PHP. Pour que le chargement automatique fonctionne, le nom de la classe et le chemin du fichier
doivent avoir une structure similaire :
Listing 4-17

1 Nom de classe:
2
Acme\HelloBundle\Controller\HelloController
3 Chemin:
4
src/Acme/HelloBundle/Controller/HelloController.php

Typiquement, le seul moment o vous devrez vous soucier du fichier app/autoload.php est quand
vous inclurez des bibliothques tierces dans le rpertoire vendor/. Pour plus d'informations sur le
chargement automatique, voir Comment charger automatiquement des classes.

Le rpertoire des sources (src/)


Pour faire simple, le rpertoire src/ contient tout le code (code PHP, templates, fichiers de configuration,
feuilles de style, etc) qui fait tourner votre application. En fait en dveloppant, le plus gros du travail sera
fait l'intrieur d'un ou plusieurs bundles que vous crerez dans ce rpertoire.
Mais qu'est-ce qu'un bundle?

Le Systme de Bundles
Un bundle est similaire aux plugins que l'on peut trouver dans d'autres logiciels, mais en mieux. La
diffrence cl est que tout est un bundle dans Symfony2, ce qui inclut le coeur du framework et le code
de votre application. Les bundles sont aux premires loges dans Symfony2. Ils vous offrent la flexibilit
d'utiliser des fonctionnalits prconstruites packages dans des bundles tiers5 ou de distribuer vos propres
bundles. Cela facilite la slection des fonctionnalits activer dans votre application et de les optimiser
comme vous le voulez.
Alors que vous apprendrez les bases ici, une entre du cookbook est entirement ddie
l'organisation et aux bonnes pratiques : bundles.

Un bundle est simplement un ensemble structur de fichiers au sein d'un rpertoire et qui implmentent
une fonctionnalit unique. Vous pourrez ainsi crer un BlogBundle, un ForumBundle ou un bundle pour
la gestion des utilisateurs (beaucoup de ces bundles existent dj et sont open source). Chaque rpertoire
contient tout ce qui est li cette fonctionnalit incluant les fichiers PHP, les templates, les feuilles de
style, le javascript, les tests et tout le reste. Chaque aspect d'une fonctionnalit se trouve dans le bundle,
et chaque fonctionnalit aussi.

5. http://knpbundles.com

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 41

Une application est faite de bundles qui sont dfinis dans la mthode registerBundles() de la classe
AppKernel :
Listing 4-18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
);
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
$bundles[] = new Acme\DemoBundle\AcmeDemoBundle();
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}

Avec la mthode registerBundles(), vous avez le contrle totale des bundles qui sont utiliss dans votre
application (incluant les bundles du coeur de Symfony).
Un bundle peut se trouver n'importe o pour peu qu'il puisse tre charg (via l'autoloader configur
app/autoload.php).

Crer un Bundle
La Symfony Standard Edition est fournie avec une tche qui cre un bundle totalement fonctionnel pour
vous. Bien sr, vous pouvez tout aussi facilement crer un bundle la main.
Pour vous montrer quel point le systme de bundle est simple, crons un nouveau bundle appel
AcmeTestBundle et activons-le.
La partie Acme est juste un nom idiot qui peut tre remplac par un autre nom qui vous reprsente
ou votre entreprise (ex ABCTestBundle pour une entreprise nomme ABC).

Commencez par crer un rpertoire src/Acme/TestBundle/ et ajoutez y un nouveau fichier appel


AcmeTestBundle.php :
Listing 4-19

1 // src/Acme/TestBundle/AcmeTestBundle.php
2 namespace Acme\TestBundle;
3
4 use Symfony\Component\HttpKernel\Bundle\Bundle;
5

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 42

6 class AcmeTestBundle extends Bundle


7 {
8 }

Le nom AcmeTestBundle suit les conventions de nommage des bundles. Vous pourriez aussi choisir
de raccourcir le nom du bundle pour TestBundle en nommant sa classe TestBundle (et en
appelant le fichier TestBundle.php).

Cette classe vide est la seule pice dont vous avez besoin afin de crer un nouveau bundle. Bien que
souvent vide, cette classe est trs puissante et peut tre utilise pour personnaliser le comportement du
bundle.
Maintenant que vous avez cr le bundle, activez-le via la classe AppKernel :
Listing 4-20

1
2
3
4
5
6
7
8
9
10
11
12
13

// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
// register your bundles
new Acme\TestBundle\AcmeTestBundle(),
);
// ...
return $bundles;
}

Et mme s'il ne fait encore rien de spcial, AcmeTestBundle est prt tre utilis.
Et bien que ce soit trs facile, Symfony fournit galement une commande qui gnre un squelette de
bundle de base :
Listing 4-21

1 $ php app/console generate:bundle --namespace=Acme/TestBundle

Le squelette du bundle contient un contrleur de base, un template et une configuration de routage qui
peuvent tre personnaliss. Vous en apprendrez plus sur les commandes Symfony2 plus tard.
Peu importe que vous criez un bundle ou que vous utilisiez un bundle tiers, assurez-vous toujours
qu'il soit activ dans registerBundles(). Si vous utilisez la commande generate:bundle, c'est
fait automatiquement pour vous.

Structure des rpertoires des bundles


La structure des rpertoires d'un bundle est simple et flexible. Par dfaut le systme de bundle suit un
ensemble de conventions qui aident garder le code homogne entre tous les bundles Symfony2. Jetez
un oeil au AcmeHelloBundle, car il contient certains des lments les plus communs d'un bundle :
Controller/ contient les contrleurs du bundle (ex HelloController.php);
DependencyInjection/ contient certaines classes d'extension d'injection de dpendances, qui
peuvent importer des configurations de services, enregistrer des passes de compilation ou plus
encore (ce rpertoire n'est pas obligatoire);

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 43

Resources/config/ contient la configuration, notamment la configuration de routage (ex


routing.yml);
Resources/views/ contient les templates organiss par nom de contrleur (ex Hello/
index.html.twig);
Resources/public/ contient les ressources web (images, feuilles de style, etc) et sont copies
ou lies par un lien symbolique dans le rpertoire de projet web/ grce la commande
assets:install;
Tests/ contient tous les tests du bundle.
Un bundle peut tre trs petit ou trs grand selon la fonctionnalit qu'il implmente. Il contient seulement
les fichiers dont vous avez besoin et rien d'autre.
En parcourant le Book, vous apprendrez comment persister des objets en base de donnes, crer et valider
des formulaires, crer des traductions pour votre application, crire des tests et bien plus encore. Chacun
de ces aspects a sa propre place et son propre rle au sein d'un bundle.

Configuration de l'Application
Une application consiste en un ensemble de bundles qui reprsente toutes les fonctionnalits et capacits
de votre application. Chaque bundle peut tre personnalis via les fichiers de configuration crits en
YAML, XML ou PHP. Par dfaut, la configuration principale se situe dans le rpertoire app/config/ et se
trouve dans un fichier appel config.yml, config.xml ou config.php selon le format que vous prfrez
:
Listing 4-22

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# app/config/config.yml
imports:
- { resource: parameters.ini }
- { resource: security.yml }
framework:
secret:
router:
#...

"%secret%"
{ resource: "%kernel.root_dir%/config/routing.yml" }

# Twig Configuration
twig:
debug:
"%kernel.debug%"
strict_variables: "%kernel.debug%"
# ...

Vous apprendrez exactement comment charger chaque fichier/format dans la prochaine section
Environnements.

Chaque entre de niveau zro comme framework ou twig dfinit la configuration d'un bundle particulier.
Par exemple, la cl framework dfinit la configuration du bundle du noyau de Symfony FrameworkBundle
et inclut la configuration pour le routage, les templates et d'autres fonctionnalits du noyau.
Pour le moment, ne vous inquitez pas des options de configuration spcifiques chaque section.
Le fichier de configuration a des valeurs par dfaut optimises. Au fur et mesure que vous lirez
et explorerez chaque recoin de Symfony2, vous en apprendrez plus sur les options de configuration
spcifiques chaque fonctionnalit.

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 44

Formats de Configuration
A travers les chapitres, tous les exemples de configuration seront montrs dans les trois formats
(YAML, XML and PHP). Chacun a ses avantages et ses inconvnients. Le choix vous appartient :
YAML: Simple, propre et lisible;
XML: Plus puissant que YAML parfois et support de l'autocompltion sur les IDE;
PHP: Trs puissant, mais moins lisible que les formats de configuration standards.

Dump de configuration par dfaut


La commande config:dump-reference a t ajoute dans la version 2.1 de Symfony
Vous pouvez dumper la configuration par dfaut d'un bundle en yaml vers la console en utilisant
la commande config:dump-reference. Voici un exemple de dump de la configuration du
FrameworkBundle :
Listing 4-23

1 app/console config:dump-reference FrameworkBundle

L'alias de l'extension (cl de configuration) peut aussi tre utilis :


Listing 4-24

1 app/console config:dump-reference framework

Lisez l'article du Cookbook : Comment exposer une configuration smantique pour un Bundle pour
avoir des informations sur l'ajout de configuration dans votre bundle.

Environnements
Une application peut tourner sous diffrents environnements. Les diffrents environnements partagent
le mme code PHP (except celui du contrleur frontal), mais utilisent une configuration diffrente.
Par exemple, l'environnement de dev enregistrera les erreurs et les warnings dans les logs, tandis que
l'environnement de prod enregistrera seulement les erreurs. Certains fichiers sont reconstruits chaque
requte en environnement de dev (pour rendre le dveloppement plus pratique), mais sont mis en cache
en environnement de prod. Tous les environnements peuvent tourner ensemble sur la mme machine et
excutent la mme application.
Un projet Symfony2 commence en gnral avec 3 environnements (dev, test et prod), la cration d'un
nouvel environnement tant trs facile. Vous pouvez voir l'application sous diffrents environnements
en changeant simplement le contrleur frontal dans votre navigateur. Pour voir l'application en
environnement de dev, accdez l'application via le contrleur frontal de dveloppement :
Listing 4-25

1 http://localhost/app_dev.php/hello/Ryan

Si vous dsirez voir comment votre application se comporterait en environnement de production, appelez
le contrleur frontal de prod :
Listing 4-26

1 http://localhost/app.php/hello/Ryan

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 45

Puisque l'environnement de prod est optimis pour la vitesse; la configuration, les routes et les templates
Twig sont compils en classes PHP et mis en cache. Quand vous voudrez voir des changements en
environnement de prod, vous aurez besoin de nettoyer ces fichiers de cache afin de permettre leur
rgnration :
Listing 4-27

1 $ php app/console cache:clear --env=prod --no-debug

Si vous ouvrez le fichier web/app.php, vous trouverez ce qui est explicitement configur en
environnement de prod :
Listing 4-28

1 $kernel = new AppKernel('prod', false);

Vous pouvez crer un nouveau contrleur frontal pour un nouvel environnement en copiant ce
fichier et en changeant prod par une autre valeur.

L'environnement de test est utilis pour lancer des tests automatiques et n'est pas accessible
directement dans un navigateur. Lisez le chapitre tests pour plus de dtails.

Configuration d'Environnement
La classe AppKernel est responsable du chargement du fichier de configuration que vous avez choisi :
Listing 4-29

1
2
3
4
5

// app/AppKernel.php
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}

Vous savez dj que l'extension .yml peut tre change en .xml ou .php si vous prfrez utiliser le format
XML ou PHP pour crire votre configuration. Notez galement que chaque environnement charge sa
propre configuration. Considrez le fichier de configuration pour l'environnement de dev.
Listing 4-30

1
2
3
4
5
6
7
8
9

# app/config/config_dev.yml
imports:
- { resource: config.yml }
framework:
router:
{ resource: "%kernel.root_dir%/config/routing_dev.yml" }
profiler: { only_exceptions: false }

# ...

La cl imports est similaire l'instruction PHP include et garantit que le fichier de configuration
principal (config.yml) est charg en premier. Le reste du fichier modifie la configuration par dfaut pour
une augmentation des logs et d'autres paramtres relatifs un environnement de dveloppement.
Les environnements de prod et de test suivent le mme modle : chaque environnement importe
le fichier de configuration de base et modifie ses valeurs pour s'adapter aux besoins spcifiques
l'environnement. C'est juste une convention, mais elle vous permet de rutiliser la plupart de la
configuration et d'en modifier une partie en fonction de l'environnement.

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 46

Rsum
Flicitations ! Vous avez maintenant eu un aperu de chaque aspect fondamental de Symfony2 et avez
dcouvert sa facilit et sa flexibilit. Et mme s'il y a encore beaucoup de fonctionnalit dcouvrir, gardez
les principes de base suivants en tte :
crer une page est un processus en trois tapes impliquant une route, un contrleur et
(optionnellement) un template.
chaque projet contient juste quelques rpertoires principaux : web/ (ressources web et
contrleurs frontaux), app/ (configuration), src/ (vos bundles), et vendor/ (bibliothques
tierces) (il y a aussi un rpertoire bin/ qui est utilis pour la mise jour des bibliothques
vendors);
chaque fonctionnalit de Symfony2 (incluant le noyau du framework) est organise dans un
bundle, qui est un ensemble structur de fichiers pour cette fonctionnalit;
la configuration de chaque bundle se trouve dans le rpertoire Ressources/config du
bundle et peut tre crite en YAML, XML ou PHP;
La configuration globale de l'application se trouve dans le rpertoire app/config;
chaque environnement est accessible via des contrleurs frontaux diffrents (ex app.php et
app_dev.php) et charge un fichier de configuration diffrent.
A partir de maintenant, chaque chapitre vous introduira de plus en plus d'outils puissants et de concepts
avancs. Plus vous connatrez Symfony2, plus vous apprcierez la flexibilit de son architecture et le
pouvoir qu'il vous donne pour dvelopper rapidement des applications.

PDF brought to you by


generated on January 14, 2015

Chapter 4: La cration de pages avec Symfony2 | 47

Chapter 5

Le Contrleur
Un contrleur est une fonction PHP que vous crez pour prendre les informations provenant de la requte
HTTP et qui construit puis retourne une rponse HTTP (sous forme d'un objet Symfony2 Response).
La rponse peut tre une page HTML, un document XML, un tableau JSON srialis, une image, une
redirection, une erreur 404 ou tout ce que vous pouvez imaginer. Le contrleur contient n'importe quelle
logique arbitraire dont votre application a besoin pour retourner le contenu d'une page.
Pour en illustrer la simplicit, jetons un oeil un contrleur Symfony2 en action. Le contrleur suivant
affiche une page qui crit simplement Hello world!:
Listing 5-1

1
2
3
4
5
6

use Symfony\Component\HttpFoundation\Response;
public function helloAction()
{
return new Response('Hello world!');
}

Le but d'un contrleur est toujours le mme : crer et retourner un objet Response. Durant ce processus,
il peut lire des informations dans la requte, charger une ressource depuis la base de donnes, envoyer
un email, ou dfinir une variable dans la session de l'utilisateur. Mais dans tous les cas, le contrleur va
finalement retourner l'objet Response qui sera dlivr au client.
Il n'y a pas de magie et aucune autre exigence prendre en compte ! Voici quelques exemples classiques :
Le contrleur A prpare un objet Response reprsentant le contenu de la page d'accueil.
Le contrleur B lit le paramtre slug contenu dans la requte pour charger une entre du blog
depuis la base de donnes et cre un objet Response affichant ce blog. Si le slug ne peut pas
tre trouv dans la base de donnes, il cre et retourne un objet Response avec un code de
statut 404.
Le contrleur C gre la soumission d'un formulaire de contact. Il lit les informations de la
requte, enregistre les informations du contact dans la base de donnes et envoie ces dernires
par email au webmaster. Enfin, il cre un objet Response qui redirige le navigateur du client
vers la page merci du formulaire de contact.

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 48

Cycle de vie Requte, Contrleur, Rponse


Chaque requte gre par un projet Symfony2 suit le mme cycle de vie. Le framework s'occupe des
tches rptitives et excute finalement un contrleur qui contient votre code applicatif personnalis :
1. Chaque requte est gre par un unique fichier contrleur frontal (par exemple: app.php ou
app_dev.php) qui initialise l'application;
2. Le Router lit l'information depuis la requte (par exemple: l'URI), trouve une route qui
correspond cette information, et lit le paramtre _controller depuis la route;
3. Le contrleur correspondant la route est excut et le code interne au contrleur cre et
retourne un objet Response;
4. Les en-ttes HTTP et le contenu de l'objet Response sont renvoys au client.
Crer une page est aussi facile que de crer un contrleur (#3) et d'implmenter une route qui y fasse
correspondre une URL (#2).
Bien que son nom est trs similaire, un contrleur frontal est diffrent des contrleurs
abords dans ce chapitre. Un contrleur frontal est un petit fichier PHP qui se situe dans
votre rpertoire web et travers lequel toutes les requtes sont diriges. Une application typique
va avoir un contrleur frontal de production (par exemple: app.php) et un contrleur frontal
de dveloppement (par exemple: app_dev.php). Vous n'aurez vraisemblablement jamais besoin
d'diter, de regarder ou de vous occuper des contrleurs frontaux dans votre application.

Un contrleur simple
Bien qu'un contrleur puisse tre n'importe quelle chose PHP appelable (une fonction, une mthode
d'un objet, ou une Closure), dans Symfony2, un contrleur est gnralement une unique mthode
l'intrieur d'un objet contrleur. Les contrleurs sont aussi appels actions.
Listing 5-2

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($name)
{
return new Response('<html><body>Hello '.$name.'!</body></html>');
}
}

Notez que le contrleur est la mthode indexAction, qui rside dans une classe contrleur
(HelloController). Ne soyez pas gn par ce nom : une classe contrleur est simplement une
manire pratique de grouper plusieurs contrleurs/actions ensemble. Typiquement, la classe
contrleur va hberger plusieurs contrleurs/actions (par exemple : updateAction, deleteAction,
etc).

Ce contrleur est relativement simple :


ligne 4: Symfony2 tire avantage de la fonctionnalit des espaces de noms ( namespaces ) de
PHP 5.3 afin de donner un espace de noms la classe entire du contrleur. Le mot-cl use
importe la classe Response, que notre contrleur doit retourner.
PDF brought to you by
generated on January 14, 2015

Chapter 5: Le Contrleur | 49

ligne 6: Le nom de la classe est la concatnation d'un nom pour la classe du contrleur (par
exemple: Hello) et du mot Controller. Ceci est une convention qui fournit une uniformit
aux contrleurs et qui leur permet d'tre rfrencs seulement par la premire partie du nom
(par exemple: Hello) dans la configuration de routage ( routing ).
ligne 8: Chaque action d'une classe contrleur se termine par Action et est rfrence dans
la configuration de routage par le nom de l'action (ex index). Dans la prochaine section,
vous allez crer une route qui fait correspondre une URI son action. Vous allez apprendre
comment les paramtres de la route (par exemple {name}) deviennent les arguments de la
mthode action ($name).
ligne 10: Le contrleur cre et retourne un objet Response.

Faire correspondre une URL un Contrleur


Le nouveau contrleur retourne une simple page HTML. Pour voir cette page dans votre navigateur, vous
avez besoin de crer une route qui va faire correspondre un pattern d'URL spcifique ce contrleur :
Listing 5-3

1 # app/config/routing.yml
2 hello:
3
path:
/hello/{name}
4
defaults: { _controller: AcmeHelloBundle:Hello:index }

Aller l'URL /hello/ryan va maintenant excuter le contrleur HelloController::indexAction()


et passer la valeur ryan en tant que variable $name. Crer une page signifie simplement crer une
mthode contrleur et une route associe.
Notez la syntaxe utilise pour faire rfrence au contrleur : AcmeHelloBundle:Hello:index. Symfony2
utilise une notation de chane de caractres flexible pour faire rfrence aux diffrents contrleurs.
C'est la syntaxe la plus commune qui spcifie Symfony2 de chercher une classe contrleur appele
HelloController dans un bundle appel AcmeHelloBundle. La mthode indexAction() est alors
excute.
Pour plus de dtails sur le format de chane de caractres utilis pour rfrencer les diffrents contrleurs,
lisez Pattern de Nommage du Contrleur.
Cet exemple place la configuration de routage directement dans le rpertoire app/config/. Il existe
une meilleure faon d'organiser vos routes : placer chacune d'entre elles dans le bundle auquel elles
appartiennent. Pour plus d'informations, lisez Inclure des Ressources Externes de Routage.

Vous pouvez en apprendre beaucoup plus sur le systme de routage en lisant le chapitre Routage.

Les paramtres de la route en tant qu'arguments du contrleur


Vous savez dj que le paramtre AcmeHelloBundle:Hello:index de _controller rfre une mthode
HelloController::indexAction() qui rside dans le bundle AcmeHelloBundle. Mais les arguments qui
sont passs cette mthode sont encore plus intressants:
Listing 5-4

1 // src/Acme/HelloBundle/Controller/HelloController.php
2 namespace Acme\HelloBundle\Controller;
3
4 use Symfony\Bundle\FrameworkBundle\Controller\Controller;

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 50

5
6 class HelloController extends Controller
7 {
8
public function indexAction($name)
9
{
10
// ...
11
}
12 }

Le contrleur possde un argument unique, $name, qui correspond au paramtre {name} de la route
associe (ryan dans notre exemple). En fait, lorsque vous excutez votre contrleur, Symfony2 fait
correspondre chaque argument du contrleur avec un paramtre de la route correspondante. Prenez
l'exemple suivant :
Listing 5-5

1 # app/config/routing.yml
2 hello:
3
path:
/hello/{first_name}/{last_name}
4
defaults: { _controller: AcmeHelloBundle:Hello:index, color: green }

Le contrleur dans cet exemple peut prendre plusieurs arguments:


Listing 5-6

1 public function indexAction($first_name, $last_name, $color)


2 {
3
// ...
4 }

Notez que les deux variables de substitution ({first_name}, {last_name}) ainsi que la variable par
dfaut color sont disponibles en tant qu'arguments dans le contrleur. Quand une route correspond, les
variables de substitution sont fusionnes avec celles par dfaut afin de construire un tableau qui est la
disposition de votre contrleur.
Faire correspondre les paramtres de la route aux arguments du contrleur est facile et flexible. Gardez
les directives suivantes en tte quand vous dveloppez.
L'ordre des arguments du contrleur n'a pas d'importance
Symfony est capable de faire correspondre les noms des paramtres de la route aux noms
des variables de la signature de la mthode du contrleur. En d'autres termes, il ralise
que le paramtre {last_name} correspond l'argument $last_name. Les arguments
du contrleur pourraient tre totalement rorganiss, cela fonctionnerait toujours
parfaitement :
Listing 5-7

1 public function indexAction($last_name, $color, $first_name)


2 {
3
// ..
4 }

Chaque argument du contrleur doit correspondre un paramtre de la route


Le code suivant lancerait une RuntimeException parce qu'il n'y a pas de paramtre foo
dfini dans la route :

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 51

Listing 5-8

1 public function indexAction($first_name, $last_name, $color, $foo)


2 {
3
// ..
4 }

Cependant, dfinir l'argument en tant qu'optionnel est parfaitement valide. L'exemple


suivant ne lancerait pas d'exception :
Listing 5-9

1 public function indexAction($first_name, $last_name, $color, $foo = 'bar')


2 {
3
// ..
4 }

Tous les paramtres de la route n'ont pas besoin d'tre des arguments de votre
contrleur
Si, par exemple, le paramtre last_name n'tait pas important pour votre contrleur,
vous pourriez compltement l'omettre :
Listing 5-10

1 public function indexAction($first_name, $color)


2 {
3
// ..
4 }

Chaque route possde aussi un paramtre spcial _route qui est gal au nom de la route qui a t
reconnue (par exemple: hello). Bien que gnralement inutile , il est nanmoins disponible en tant
qu'argument du contrleur au mme titre que les autres.

La Requte en tant qu'argument du Contrleur


Pour plus de facilits, Symfony peut aussi vous passer l'objet Request en tant qu'argument de votre
contrleur. Ceci est spcialement pratique lorsque vous travaillez avec les formulaires, par exemple:
Listing 5-11

1
2
3
4
5
6
7
8
9

use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
$form = $this->createForm(...);
$form->handleRequest($request);
// ...
}

Crer une page statique


Il est possible de crer une page web statique sans mme crer de contrleur (seuls un template et une
route sont ncessaires).
PDF brought to you by
generated on January 14, 2015

Chapter 5: Le Contrleur | 52

N'hsitez pas l'utiliser ! Rendez-vous sur la page de cookbook Comment rendre un template sans passer
par un contrleur.

La Classe Contrleur de Base


Afin de vous faciliter le travail, Symfony2 est fourni avec une classe Controller de base qui vous assiste
dans les tches les plus communes et qui donne votre classe contrleur l'accs n'importe quelle
ressource dont elle pourrait avoir besoin. En tendant cette classe Controller, vous pouvez tirer parti de
plusieurs mthodes utiles.
Ajoutez le mot-cl use au-dessus de la classe Controller et modifiez HelloController pour qu'il
l'tende:
Listing 5-12

1
2
3
4
5
6
7
8
9
10
11
12
13

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController extends Controller
{
public function indexAction($name)
{
return new Response('<html><body>Hello '.$name.'!</body></html>');
}
}

Cela ne change en fait rien au fonctionnement de votre contrleur. Dans la prochaine section, vous
en apprendrez plus sur les mthodes d'aide ( helper ) que la classe contrleur de base met votre
disposition. Ces mthodes sont juste des raccourcis pour utiliser des fonctionnalits coeurs de Symfony2
qui sont votre disposition en utilisant ou non la classe Controller de base. Une bonne manire de se
rendre compte de son efficacit est de regarder le code de la classe Controller1 elle-mme.
tendre la classe de base est facultatif dans Symfony; elle contient des raccourcis utiles mais rien
d'obligatoire. Vous pouvez aussi tendre ContainerAware2. L'objet conteneur de service ( service
container ) sera ainsi accessible travers la proprit container.

Vous pouvez aussi dfinir vos Contrleurs en tant que Services. Ce n'est pas obligatoire mais
cela vous permet de mieux contrler les dpendances qui sont effectivement injectes dans votre
contrleur.

Les Tches Communes du Contrleur


Bien qu'un contrleur puisse tout faire en thorie, la plupart d'entre-eux vont accomplir les mmes tches
basiques encore et toujours. Ces tches, comme rediriger, forwarder, afficher des templates et accder
aux services sont trs faciles grer dans Symfony2.

1. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/Controller/Controller.html
2. http://api.symfony.com/2.6/Symfony/Component/DependencyInjection/ContainerAware.html

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 53

Rediriger
Si vous voulez rediriger l'utilisateur sur une autre page, utilisez la mthode redirect():
Listing 5-13

1 public function indexAction()


2 {
3
return $this->redirect($this->generateUrl('homepage'));
4 }

La mthode generateUrl() est juste une fonction d'aide qui gnre une URL pour une route donne.
Pour plus d'informations, lisez le chapitre Routage.
Par dfaut, la mthode redirect() produit une redirection 302 (temporaire). Afin d'excuter une
redirection 301 (permanente), modifiez le second argument:
Listing 5-14

1 public function indexAction()


2 {
3
return $this->redirect($this->generateUrl('homepage'), 301);
4 }

La mthode redirect() est simplement un raccourci qui cre un objet Response spcialis dans
la redirection d'utilisateur. Cela revient faire:
Listing 5-15

1 use Symfony\Component\HttpFoundation\RedirectResponse;
2
3 return new RedirectResponse($this->generateUrl('homepage'));

Forwarder
Vous pouvez aussi facilement forwarder sur un autre contrleur en interne avec la mthode forward().
Plutt que de rediriger le navigateur de l'utilisateur, elle effectue une sous-requte interne, et appelle le
contrleur spcifi. La mthode forward() retourne l'objet Response qui est retourn par ce contrleur:
Listing 5-16

1 public function indexAction($name)


2 {
3
$response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
4
'name' => $name,
5
'color' => 'green',
6
));
7
8
// ... modifiez encore la rponse ou bien retournez-la directement
9
10
return $response;
11 }

Notez que la mthode forward() utilise la mme reprsentation de chane de caractres du contrleur
que celle utilise dans la configuration de routage. Dans ce cas, la classe contrleur cible va tre
HelloController dans le bundle AcmeHelloBundle. Le tableau pass la mthode devient les arguments
du contrleur. Cette mme interface est utilise lorsque vous intgrez des contrleurs dans des templates
(voir Contrleurs imbriqus). La mthode contrleur cible devrait ressembler quelque chose comme:
Listing 5-17

1 public function fancyAction($name, $color)


2 {

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 54

3
4 }

// ... cre et retourne un objet Response

Et comme quand vous crez un contrleur pour une route, l'ordre des arguments de fancyAction n'a
pas d'importance. Symfony2 fait correspondre le nom des cls d'index (par exemple: name) avec le nom
des arguments de la mthode (par exemple: $name). Si vous changez l'ordre des arguments, Symfony2 va
toujours passer la valeur correcte chaque variable.
Comme d'autres mthodes de base de Controller, la mthode forward est juste un raccourci vers
une fonctionnalit coeur de Symfony2. Un forward peut tre excut directement via le service
http_kernel et retourne un objet Response :
Listing 5-18

1 $httpKernel = $this->container->get('http_kernel');
2 $response = $httpKernel->forward('AcmeHelloBundle:Hello:fancy', array(
3
'name' => $name,
4
'color' => 'green',
5 ));

Afficher des Templates


Bien que ce n'est pas obligatoire, la plupart des contrleurs vont finalement retourner un template qui
sera charg de gnrer du HTML (ou un autre format) pour le contrleur. La mthode renderView()
retourne un template et affiche son contenu. Le contenu du template peut tre utilis pour crer un objet
Response:
Listing 5-19

1
2
3
4
5
6
7
8

use Symfony\Component\HttpFoundation\Response;
$content = $this->renderView(
'AcmeHelloBundle:Hello:index.html.twig',
array('name' => $name)
);
return new Response($content);

Cela peut mme tre effectu en une seule tape l'aide de la mthode render(), qui retourne un objet
Response contenant le contenu du template:
Listing 5-20

1 return $this->render(
2
'AcmeHelloBundle:Hello:index.html.twig',
3
array('name' => $name)
4 );

Dans les deux cas, le template Resources/views/Hello/index.html.twig dans AcmeHelloBundle sera


affich.
Le moteur de rendu ( templating engine ) de Symfony est expliqu plus en dtail dans le chapitre
Templating
Vous pouvez mme viter d'appeler la mthode render en utilisant l'annotation @Template. Lisez
la documentation du FrameworkExtraBundle pour plus de dtails.

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 55

La mthode renderView est un raccourci vers l'utilisation directe du service templating. Ce


dernier peut aussi tre utilis directement:
Listing 5-21

1 $templating = $this->get('templating');
2 $content = $templating->render(
3
'AcmeHelloBundle:Hello:index.html.twig',
4
array('name' => $name)
5 );

Il est aussi possible d'afficher des templates situs dans des sous-rpertoires. Mais vitez tout de
mme de tomber dans la facilit de faire des arborescences trop labores:
Listing 5-22

1 $templating->render(
2
'AcmeHelloBundle:Hello/Greetings:index.html.twig',
3
array('name' => $name)
4 );
5 // Affiche index.html.twig situ dans Resources/views/Hello/Greetings.

Accder d'autres Services


Quand vous tendez la classe contrleur de base, vous pouvez utiliser n'importe quel service Symfony2
via la mthode get(). Voici plusieurs services communs dont vous pourriez avoir besoin:
Listing 5-23

1 $templating = $this->get('templating');
2
3 $router = $this->get('router');
4
5 $mailer = $this->get('mailer');

Il y a d'innombrables autres services votre disposition et vous tes encourag dfinir les vtres. Pour
lister tous les services disponibles, utilisez la commande de la console container:debug :
Listing 5-24

1 $ php app/console container:debug

Pour plus d'informations, voir le chapitre Service Container.

Grer les Erreurs et les Pages 404


Quand quelque chose n'est pas trouv, vous devriez vous servir correctement du protocole HTTP et
retourner une rponse 404. Pour ce faire, vous allez lancer un type spcial d'exception. Si vous tendez la
classe contrleur de base, faites comme a:
Listing 5-25

1 public function indexAction()


2 {
3
// rcuprer l'objet depuis la base de donnes
4
$product = ...;
5
if (!$product) {
6
throw $this->createNotFoundException('Le produit n\'existe pas');
7
}
8

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 56

9
10 }

return $this->render(...);

La mthode createNotFoundException() cre un objet spcial NotFoundHttpException, qui


finalement dclenche une rponse HTTP 404 dans Symfony.
videmment, vous tes libre de lever n'importe quelle Exception dans votre contrleur - Symfony2
retournera automatiquement un code de rponse HTTP 500.
Listing 5-26

1 throw new \Exception('Quelque chose a mal tourn!');

Dans chaque cas, une page d'erreur avec style est retourne l'utilisateur final et une page d'erreur
complte avec des infos de debugging est retourne au dveloppeur (lorsqu'il affiche cette page en mode
debug). Ces deux pages d'erreur peuvent tre personnalises. Pour de plus amples dtails, lisez la partie
du cookbook Comment personnaliser les pages d'erreur .

Grer la Session
Symfony2 fournit un objet session sympa que vous pouvez utiliser pour stocker de l'information propos
de l'utilisateur (que ce soit une personne relle utilisant un navigateur, un bot, ou un service web) entre
les requtes. Par dfaut, Symfony2 stocke les attributs dans un cookie en utilisant les sessions natives de
PHP.
Stocker et rcuprer des informations depuis la session peut tre effectu facilement depuis n'importe
quel contrleur:
Listing 5-27

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
$session = $request->getSession();

// stocke un attribut pour une rutilisation lors d'une future requte utilisateur
$session->set('foo', 'bar');
// dans un autre contrleur pour une autre requte
$foo = $session->get('foo');
// utilise une valeur par dfaut si la cl n'existe pas
$filters = $session->get('filters', array());
}

Ces attributs vont rester affects cet utilisateur pour le restant de son temps session.

Les Messages Flash


Vous pouvez aussi stocker de petits messages qui vont tre gards dans la session de l'utilisateur pour la
requte suivante uniquement. Ceci est utile lors du traitement d'un formulaire : vous souhaitez rediriger
l'utilisateur et afficher un message spcial lors de la prochaine requte. Ces types de message sont appels
messages flash .
Par exemple, imaginez que vous traitiez la soumission d'un formulaire:
Listing 5-28

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 57

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
$form = $this->createForm(...);
$form->handleRequest($request);
if ($form->isValid()) {
// effectue le traitement du formulaire
$this->get('session')->getFlashBag()->add(
'notice',
'Vos changements ont t sauvegards!'
);
return $this->redirect($this->generateUrl(...));
}
return $this->render(...);
}

Aprs avoir trait la requte, le contrleur dfinit un message flash notice et puis redirige l'utilisateur. Le
nom (notice) n'est pas trs important - c'est juste ce que vous utilisez pour identifier le type du message.
Dans le template de la prochaine action, le code suivant pourra tre utilis pour afficher le message
notice :
Listing 5-29

1 {% for flashMessage in app.session.flashbag.get('notice') %}


2
<div class="flash-notice">
3
{{ flashMessage }}
4
</div>
5 {% endfor %}

De par leur conception, les messages flash sont faits pour durer pendant exactement une requte (ils
disparaissent en un clair/flash ). Ils sont conus pour tre utiliss avec les redirections exactement
comme vous l'avez fait dans cet exemple.

L'Objet Response
La seule condition requise d'un contrleur est de retourner un objet Response. La classe Response3 est
une abstraction PHP autour de la rponse HTTP - le message texte est complt avec des en-ttes HTTP
et du contenu qui est envoy au client:
Listing 5-30

1
2
3
4
5
6
7
8

use Symfony\Component\HttpFoundation\Response;

// cre une simple Rponse avec un code de statut 200 (celui par dfaut)
$response = new Response('Hello '.$name, 200);
// cre une rponse JSON avec un code de statut 200
$response = new Response(json_encode(array('name' => $name)));
$response->headers->set('Content-Type', 'application/json');

3. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 58

La proprit headers est un objet HeaderBag4 avec plusieurs mthodes utiles pour lire et
transformer les en-ttes de la Response. Les noms des en-ttes sont normaliss et ainsi, utiliser
Content-Type est quivalent content-type ou mme content_type.

Il existe des classes spciales pour construire des rponses plus facilement:
Pour du JSON, JsonResponse5. Lisez Crer une rponse JSON.
Pour des fichiers, BinaryFileResponse6. Lisez Retourner des fichiers.

L'Objet Request
En plus des paramtres de routes, le contrleur a aussi accs l'objet Request quand il tend la classe
Controller de base:
Listing 5-31

1
2
3
4
5
6
7
8
9
10
11
12
13
14

use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
$request->isXmlHttpRequest(); // est-ce une requte Ajax?
$request->getPreferredLanguage(array('en', 'fr'));
$request->query->get('page'); // retourne un paramtre $_GET
$request->request->get('page'); // retourne un paramtre $_POST
}

Comme l'objet Response, les en-ttes de la requte sont stocks dans un objet HeaderBag et sont
facilement accessibles.

Le mot de la fin
Chaque fois que vous crez une page, vous allez au final avoir besoin d'crire du code qui contient la
logique de cette page. Dans Symfony, ceci est appel un contrleur, et c'est une fonction PHP qui peut
faire tout ce qu'il faut pour retourner l'objet final Response qui sera dlivr l'utilisateur.
Pour vous simplifier la vie, vous pouvez choisir d'tendre une classe Controller de base, qui contient
des mthodes raccourcies pour de nombreuses tches communes d'un contrleur. Par exemple, sachant
que vous ne voulez pas mettre de code HTML dans votre contrleur, vous pouvez utiliser la mthode
render() pour dlivrer et retourner le contenu d'un template.
Dans d'autres chapitres, vous verrez comment le contrleur peut tre utilis pour sauvegarder et aller
chercher des objets dans une base de donnes, traiter des soumissions de formulaires, grer le cache et
plus encore.

4. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/HeaderBag.html
5. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/JsonResponse.html
6. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/BinaryFileResponse.html

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 59

En savoir plus grce au Cookbook


Comment personnaliser les pages d'erreur
Comment dfinir des contrleurs en tant que Services

PDF brought to you by


generated on January 14, 2015

Chapter 5: Le Contrleur | 60

Chapter 6

Routage
De belles URLs sont une obligation absolue pour une quelconque application web srieuse. Cela
implique d'oublier les URLs moches comme index.php?article_id=57 en faveur de quelque chose
comme /read/intro-to-symfony.
Avoir de la flexibilit est encore plus important. Que se passe-t-il si vous avez besoin de changer l'URL
d'une page de /blog pour /news ? Combien de liens aurez-vous besoin de traquer et mettre jour afin de
prendre en compte ce changement ? Si vous utilisez le routeur de Symfony, le changement est simple.
Le routeur Symfony2 vous laisse dfinir des URLs cratives que vous faites correspondre diffrents
points de votre application. A la fin de ce chapitre, vous serez capable de :

Crer des routes complexes qui correspondent des contrleurs


Gnrer des URLs l'intrieur des templates et des contrleurs
Charger des ressources de routage depuis des bundles (ou depuis ailleurs)
Dbugger vos routes

Le routage en Action
Une route est une correspondance entre un pattern d'URL et un contrleur. Par exemple, supposez que
vous vouliez faire correspondre n'importe quelle URL comme /blog/my-post ou /blog/all-aboutsymfony et l'envoyer vers un contrleur qui puisse chercher et retourner ce billet de blog. La route est
simple :
Listing 6-1

1 # app/config/routing.yml
2 blog_show:
3
path: /blog/{slug}
4
defaults: { _controller: AcmeBlogBundle:Blog:show }

Le path dfini par la route blog_show agit comme /blog/* o le joker (l'toile) possde le nom slug.
Pour l'URL /blog/my-blog-post, la variable slug prend la valeur de my-blog-post, qui est votre
disposition dans votre contrleur (continuez lire).
Le paramtre _controller est une cl spciale qui dit Symfony quel contrleur doit tre excut
lorsqu'une URL correspond cette route. La chane de caractres _controller est appele le nom
logique. Il suit un pattern qui pointe vers une classe et une mthode PHP spcifique:
PDF brought to you by
generated on January 14, 2015

Chapter 6: Routage | 61

Listing 6-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// src/Acme/BlogBundle/Controller/BlogController.php
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BlogController extends Controller
{
public function showAction($slug)
{
$blog = // utilisez la variable $slug pour interroger la base de donnes
return $this->render('AcmeBlogBundle:Blog:show.html.twig', array(
'blog' => $blog,
));
}
}

Flicitations ! Vous venez juste de crer votre premire route et de la connecter un contrleur.
Maintenant, quand vous visitez /blog/my-post, le contrleur showAction va tre excut et la variable
$slug aura la valeur my-post.
Ceci est le but du routeur Symfony2 : faire correspondre l'URL d'une requte un contrleur. Tout au
long du chemin, vous allez apprendre toutes sortes d'astuces qui rendent mme facile la cration des
URLs les plus complexes.

Routage: Sous le Capot


Quand une requte est lance vers votre application, elle contient une adresse pointant sur la ressource
exacte que le client dsire. Cette adresse est appele l'URL, (ou l'URI), et pourrait tre /contact, /blog/
read-me, ou n'importe quoi d'autre. Prenez l'exemple de la requte HTTP suivante :
Listing 6-3

1 GET /blog/my-blog-post

Le but du systme de routage de Symfony2 est d'analyser cette URL et de dterminer quel contrleur
devrait tre excut. Le droulement complet ressemble a :
1. La requte est gre par le contrleur frontal de Symfony2 (par exemple : app.php) ;
2. Le coeur de Symfony2 (c-a-d Kernel) demande au routeur d'inspecter la requte ;
3. Le routeur fait correspondre l'URL entrante une route spcifique et retourne l'information
propos de la route, incluant le contrleur qui devrait tre excut ;
4. Le Kernel Symfony2 excute le contrleur, qui finalement retourne un objet Response.

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 62

La partie routage est un outil qui traduit l'URL entrante en un contrleur spcifique excuter.

Crer des Routes


Symfony charge toutes les routes de votre application depuis un fichier unique de configuration du
routage. Le fichier est gnralement localis dans le rpertoire app/config/routing.yml, mais peut
tre configur afin d'tre n'importe quoi d'autre (incluant un fichier XML ou PHP) via le fichier de
configuration de l'application :
Listing 6-4

1 # app/config/config.yml
2 framework:
3
# ...
4
router:
{ resource: "%kernel.root_dir%/config/routing.yml" }

Bien que toutes les routes soient charges depuis un fichier unique, c'est une pratique courante
d'inclure des ressources de routage additionnelles. Pour faire cela, il vous suffit de spcifier quels
fichiers externes doivent tre inclus dans le fichier de configuration de routage principal. Rfrezvous la section Inclure des Ressources Externes de Routage pour plus d'informations.

Configuration Basique des Routes


Dfinir une route est facile, et une application typique va avoir de nombreuses routes. Une route basique
consiste simplement de deux parties : le pattern comparer et un tableau defaults :
Listing 6-5

1 _welcome:
2
path: /
3
defaults:

{ _controller: AcmeDemoBundle:Main:homepage }

Cette route correspond la page d'accueil (/) et la relie avec le contrleur


AcmeDemoBundle:Main:homepage. La chane de caractres _controller est traduite par Symfony2 en
une fonction PHP qui est ensuite excute. Ce processus sera expliqu rapidement dans la section Pattern
de Nommage du Contrleur.

Routage avec les Paramtres de substitution


videmment, le systme de routage supporte des routes beaucoup plus intressantes. De nombreuses
routes vont contenir un ou plusieurs paramtres de substitution nomms joker :
Listing 6-6

1 blog_show:
2
path: /blog/{slug}
3
defaults: { _controller: AcmeBlogBundle:Blog:show }

Le pattern va faire correspondre tout ce qui ressemble /blog/*. Mieux encore, la valeur correspondante
au paramtre de substitution {slug} sera disponible dans votre contrleur. En d'autres termes, si l'URL
est /blog/hello-world, une variable $slug, avec la valeur hello-world, sera votre disposition dans
le contrleur. Ceci peut tre utilis, par exemple, pour rcuprer l'entre du blog qui correspond cette
chane de caractres.
Cependant, le pattern ne va pas faire correspondre simplement /blog cette route. Et cela parce que, par
dfaut, tous les paramtres de substitution sont requis. Ce comportement peut tre modifi en ajoutant
une valeur au paramtre de substitution dans le tableau defaults.

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 63

Paramtres de substitution Requis et Optionnels


Pour rendre les choses plus excitantes, ajoutez une nouvelle route qui affiche une liste de toutes les
entres du blog disponibles pour cette application imaginaire :
Listing 6-7

1 blog:
2
path: /blog
3
defaults: { _controller: AcmeBlogBundle:Blog:index }

Jusqu'ici, cette route est aussi simple que possible - elle ne contient pas de paramtres de substitution et
va correspondre uniquement l'URL exacte /blog. Mais que se passe-t-il si vous avez besoin que cette
route supporte la pagination, o blog/2 affiche la seconde page des entres du blog ? Mettez la route
jour afin qu'elle ait un nouveau paramtre de substitution {page} :
Listing 6-8

1 blog:
2
path: /blog/{page}
3
defaults: { _controller: AcmeBlogBundle:Blog:index }

Comme le paramtre de substitution {slug} d'avant, la valeur correspondante {page} va tre


disponible dans votre contrleur. Cette dernire peut tre utilise pour dterminer quel ensemble
d'entres blog doit tre dlivr pour la page donne.
Mais attendez ! Sachant que les paramtres substitutifs sont requis par dfaut, cette route va maintenant
arrter de correspondre une requte contenant simplement l'URL /blog. A la place, pour voir la page
1 du blog, vous devriez utiliser l'URL /blog/1 ! Ceci n'tant pas une solution viable pour une telle
application web, modifiez la route et faites en sorte que le paramtre {page} soit optionnel. Vous pouvez
faire cela en l'incluant dans la collection des defaults :
Listing 6-9

1 blog:
2
path: /blog/{page}
3
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }

En ajoutant page aux cls defaults, le paramtre substitutif {page} n'est donc plus obligatoire. L'URL
/blog va correspondre cette route et la valeur du paramtre page sera dfini comme tant 1. L'URL
/blog/2 quant elle va aussi correspondre, donnant au paramtre page la valeur 2. Parfait.
/blog

{page} = 1

/blog/1

{page} = 1

/blog/2

{page} = 2

Les routes avec des paramtres optionnels la fin ne correspondront pas la requte demande s'il
y a un slash la fin (ex /blog/ ne correspondra pas, /blog correspondra).

Ajouter des Conditions Requises


Regardez rapidement les routes qui ont t cres jusqu'ici :
Listing 6-10

1 blog:
2
path: /blog/{page}
3
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
4

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 64

5 blog_show:
6
path: /blog/{slug}
7
defaults: { _controller: AcmeBlogBundle:Blog:show }

Pouvez-vous voir le problme ? Notez que les deux routes possdent des patterns qui correspondent aux
URLs ressemblant /blog/*. Le routeur Symfony choisira toujours la premire route correspondante
qu'il trouve. En d'autres mots, la route blog_show ne sera jamais utilise. Nanmoins, une URL comme
/blog/my-blog-post correspondra la premire route (blog) qui retournera une valeur (n'ayant aucun
sens) my-blog-post au paramtre {page}.
URL

route paramtres

/blog/2

blog

{page} = 2

/blog/my-blog-post

blog

{page} = my-blog-post

La rponse au problme est d'ajouter des conditions requises la route. Les routes de l'exemple ci-dessus
fonctionneraient parfaitement si le pattern /blog/{page} correspondait uniquement aux URLs avec la
partie {page} tant un nombre entier. Heureusement, des conditions requises sous forme d'expression
rgulire peuvent tre facilement ajoutes pour chaque paramtre. Par exemple :
Listing 6-11

1 blog:
2
path: /blog/{page}
3
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
4
requirements:
5
page: \d+

La condition requise \d+ est une expression rgulire qui oblige la valeur du paramtre {page} tre un
nombre (compos d'un ou plusieurs chiffres). La route blog correspondra toujours une URL comme
/blog/2 (parce que 2 est un nombre), mais elle ne correspondra par contre plus une URL comme
/blog/my-blog-post (car my-blog-post n'est pas un nombre).
Comme rsultat, une URL comme /blog/my-blog-post va dornavant correspondre correctement la
route blog_show.
URL

route

paramtres

/blog/2

blog

{page} = 2

/blog/my-blog-post

blog_show {slug} = my-blog-post

Les Routes prcdentes Gagnent toujours


Tout cela signifie que l'ordre des routes est trs important. Si la route blog_show tait place
au-dessus de la route blog, l'URL /blog/2 correspondrait blog_show au lieu de blog puisque
le paramtre {slug} de blog_show n'a pas de conditions requises. En utilisant un ordre clair et
intelligent, vous pouvez accomplir tout ce que vous voulez.

Avec la possibilit de dfinir des conditions requises pour les paramtres l'aide d'expressions rgulires,
la complexit et la flexibilit de chaque condition est entirement dpendante de ce que vous en faites.
Supposez que la page d'accueil de votre application soit disponible en deux langues diffrentes, base sur
l'URL :
Listing 6-12

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 65

1 homepage:
2
path: /{culture}
3
defaults: { _controller: AcmeDemoBundle:Main:homepage, culture: en }
4
requirements:
5
culture: en|fr

Pour les requtes entrantes, la partie {culture} de l'URL est compare l'expression rgulire (en|fr).
/

{culture} = en

/en {culture} = en
/fr

{culture} = fr

/es

ne correspondra pas cette route

Ajouter des Conditions Requises pour la Mthode HTTP


En plus de l'URL, vous pouvez aussi comparer la mthode (c-a-d GET, HEAD, POST, PUT, DELETE)
de la requte entrante avec celle dfinie dans les conditions requises de la route. Supposez que vous ayez
un formulaire de contact avec deux contrleurs - un pour afficher le formulaire (quand on a une requte
GET) et un pour traiter le formulaire une fois qu'il a t soumis (avec une requte POST). Ceci peut tre
accompli avec la configuration de routage suivante :
Listing 6-13

1 contact:
2
path: /contact
3
defaults: { _controller: AcmeDemoBundle:Main:contact }
4
requirements:
5
_method: GET
6
7 contact_process:
8
path: /contact
9
defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
10
requirements:
11
_method: POST

Malgr le fait que ces deux routes aient des patterns identiques (/contact), la premire route
correspondra uniquement aux requtes GET et la seconde route correspondra seulement aux requtes
POST. Cela signifie que vous pouvez afficher le formulaire et le soumettre via la mme URL, tout en
utilisant des contrleurs distincts pour les deux actions.
Si aucune condition requise _method n'est spcifie, la route correspondra toutes les mthodes.

Comme les autres, la condition requise _method est analyse en tant qu'expression rgulire. Pour faire
correspondre les requtes la mthode GET ou POST, vous pouvez utiliser GET|POST.

Ajouter un Pattern Hostname ( nom d'hte en franais)


New in version 2.2.

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 66

Vous pouvez aussi faire la correspondance avec le hostname ( nom d'hte en franais) HTTP de la
requte entrante :
Listing 6-14

1 mobile_homepage:
2
path: /
3
host: m.example.com
4
defaults: { _controller: AcmeDemoBundle:Main:mobileHomepage }
5
6 homepage:
7
path: /
8
defaults: { _controller: AcmeDemoBundle:Main:homepage }

Les deux routes correspondent au mme pattern /, cependant la premire va correspondre uniquement
si le nom d'hte est m.example.com.

Valeurs de substitution et Conditions Requises pour les Patterns Hostname (


nom d'hte en franais)
Des valeurs de substitution peuvent tre utilises dans les patterns de noms d'hte ainsi que dans les
patterns, et les conditions requises s'appliquent aussi elles.
Dans l'exemple suivant, nous vitons de coder en dur ( hardcoding en anglais) le nom de domaine en
utilisant une valeur de substitution et une condition requise. %domain% dans les conditions requises est
remplac par la valeur du paramtre domain venant du conteneur d'injection de dpendances.
Listing 6-15

mobile_homepage:
path: /
host: m.{domain}
defaults:
_controller: AcmeDemoBundle:Main:mobileHomepage
domain: %domain%
homepage:
path: /
defaults: { _controller: AcmeDemoBundle:Main:homepage }

Exemple de Routage Avanc


A ce point, vous connaissez tout ce dont vous avez besoin pour crer une structure de routage puissante
dans Symfony. Ce qui suit est un exemple montrant simplement quel point le systme de routage peut
tre flexible :
Listing 6-16

1 article_show:
2
path: /articles/{culture}/{year}/{title}.{_format}
3
defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
4
requirements:
5
culture: en|fr
6
_format: html|rss
7
year:
\d+

Comme vous l'avez vu, cette route correspondra uniquement si la partie {culture} de l'URL est en ou
fr et si {year} est un nombre. Cette route montre aussi comment vous pouvez utiliser un point entre
les paramtres de substitution la place d'un slash. Les URLs qui correspondent cette route pourraient
ressembler a :

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 67

/articles/en/2010/my-post
/articles/fr/2010/my-post.rss

Le Paramtre Spcial de Routage _format


Cet exemple met aussi en valeur le paramtre spcial de routage _format. Lorsque vous utilisez ce
paramtre, la valeur correspondante devient alors le format de la requte de l'objet Request.
Finalement, le format de la requte est utilis pour des taches comme spcifier le Content-Type
de la rponse (par exemple : un format de requte json se traduit en un Content-Type ayant pour
valeur application/json). Il peut aussi tre utilis dans le contrleur pour dlivrer un template
diffrent pour chaque valeur de _format. Le paramtre _format est une manire trs puissante de
dlivrer le mme contenu dans diffrents formats.

Paramtres spciaux de routing


Comme vous l'avez vu, chaque paramtre ou valeur par dfaut peut tre pass comme argument la
mthode contrleur. De plus, il y a 3 paramtres spciaux : chacun d'eux apporte une fonctionnalit
unique votre application :
_controller: Comme vous l'avez vu, ce paramtre est utilis pour dterminer quel contrleur
est excut lorsque l'URL est reconnue ;
_format: Utilis pour dfinir le format de la requte(en savoir plus) ;
_locale: Utilis pour dfinir la locale de la session (en savoir plus).
Si vous utilisez le paramtre _locale dans une route, cette valeur sera galement stocke en session
pour que les futures requtes la conservent.

Pattern de Nommage du Contrleur


Chaque route doit avoir un paramtre _controller, qui lui dicte quel contrleur doit tre excut
lorsque cette route correspond l'URL. Ce paramtre utilise un pattern de chane de caractres simple
appel nom logique du contrleur, que Symfony fait correspondre une mthode et une classe PHP
spcifique. Le pattern a trois parties, chacune spare par deux-points :
bundle:contrleur:action
Par exemple, la valeur AcmeBlogBundle:Blog:show pour le paramtre _controller signifie :
Bundle

Classe du Contrleur

Nom de la Mthode

AcmeBlogBundle

BlogController

showAction

Le contrleur pourrait ressembler quelque chose comme a:


Listing 6-17

1 // src/Acme/BlogBundle/Controller/BlogController.php
2 namespace Acme\BlogBundle\Controller;
3
4 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
5

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 68

6 class BlogController extends Controller


7 {
8
public function showAction($slug)
9
{
10
// ...
11
}
12 }

Notez que Symfony ajoute la chane de caractres Controller au nom de la classe (Blog =>
BlogController) et Action au nom de la mthode (show => showAction).
Vous pourriez aussi faire rfrence ce contrleur en utilisant le nom complet de sa classe et de sa
mthode : Acme\BlogBundle\Controller\BlogController::showAction. Mais si vous suivez quelques
conventions simples, le nom logique est plus concis et permet aussi plus de flexibilit.
En plus d'utiliser le nom logique ou le nom complet de la classe, Symfony supporte une troisime
manire de rfrer un contrleur. Cette mthode utilise un seul sparateur deux-points (par
exemple : service_name:indexAction) et rfre au contrleur en tant que service (see Comment
dfinir des contrleurs en tant que Services).

Les Paramtres de la Route et les Arguments du Contrleur


Les paramtres de la route (par exemple : {slug}) sont spcialement importants parce que chacun d'entre
eux est mis disposition en tant qu'argument de la mthode contrleur:
Listing 6-18

1 public function showAction($slug)


2 {
3
// ...
4 }

En ralit, la collection entire defaults est fusionne avec les valeurs des paramtres afin de former un
unique tableau. Chaque cl du tableau est disponible en tant qu'argument dans le contrleur.
En d'autres termes, pour chaque argument de votre mthode contrleur, Symfony recherche un
paramtre de la route avec ce nom et assigne sa valeur cet argument. Dans l'exemple avanc ci-dessus,
n'importe quelle combinaison (dans n'importe quel ordre) des variable suivantes pourrait tre utilise en
tant qu'arguments de la mthode showAction() :

$culture
$year
$title
$_format
$_controller

Sachant que les paramtres de substitution et la collection defaults sont fusionns ensemble, mme la
variable $_controller est disponible. Pour une discussion plus dtaille sur le sujet, lisez Les paramtres
de la route en tant qu'arguments du contrleur.
Vous pouvez aussi utiliser une variable $_route spciale, qui est dfinie comme tant le nom de la
route qui a correspondu.

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 69

Inclure des Ressources Externes de Routage


Toutes les routes sont charges via un unique fichier de configuration - gnralement app/config/
routing.yml (voir Crer des Routes ci-dessus). Cependant, la plupart du temps, vous voudrez charger
les routes depuis d'autres endroits, comme un fichier de routage qui se trouve dans un bundle. Ceci peut
tre fait en important ce fichier :
Listing 6-19

1 # app/config/routing.yml
2 acme_hello:
3
resource: "@AcmeHelloBundle/Resources/config/routing.yml"

Lorsque vous importez des ressources depuis YAML, la cl (par exemple : acme_hello) n'a pas de
sens prcis. Assurez-vous simplement que cette dernire soit unique afin qu'aucune autre ligne ne
la surcharge.

La cl de la ressource charge la ressource de routage donne. Dans cet exemple, la ressource est le
rpertoire entier d'un fichier, o la syntaxe raccourcie @AcmeHelloBundle est traduite par le rpertoire de
ce bundle. Le fichier import pourrait ressembler quelque chose comme a :
Listing 6-20

1 # src/Acme/HelloBundle/Resources/config/routing.yml
2 acme_hello:
3
path: /hello/{name}
4
defaults: { _controller: AcmeHelloBundle:Hello:index }

Les routes de ce fichier sont analyses et charges de la mme manire que pour le fichier de routage
principal.

Prfixer les Routes Importes


Vous pouvez aussi choisir de dfinir un prfixe pour les routes importes. Par exemple, supposez que
vous vouliez que la route acme_hello ait un pattern final /admin/hello/{name} la place de simplement
/hello/{name} :
Listing 6-21

1 # app/config/routing.yml
2 acme_hello:
3
resource: "@AcmeHelloBundle/Resources/config/routing.yml"
4
prefix:
/admin

La chane de caractres /admin sera maintenant ajoute devant le pattern de chaque route charge depuis
la nouvelle ressource de routage.

Ajouter un Pattern Hostname ( nom d'hte en franais) aux Routes Importes


New in version 2.2.

Vous pouvez dfinir un pattern de nom d'hte pour les routes importes :
Listing 6-22

1 # app/config/routing.yml
2 acme_hello:

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 70

3
4

resource: "@AcmeHelloBundle/Resources/config/routing.yml"
host: "hello.example.com"

Le pattern de nom d'hte hello.example.com sera dfini pour chaque route charge depuis la nouvelle
ressource de routage.
Vous pouvez galement dfinir les routes en utilisant les annotations. Lisez la documentation du
FrameworkExtraBundle pour savoir comment faire.

Visualiser et Debugger les Routes


Lorsque vous ajoutez et personnalisez des routes, cela aide beaucoup de pouvoir visualiser et d'avoir des
informations dtailles propos de ces dernires. Une manire gniale de voir chaque route de votre
application est d'utiliser la commande de la console router:debug. Excutez la commande suivante
depuis la racine de votre projet.
Listing 6-23

1 $ php app/console router:debug

La commande va retourner une liste de toutes les routes configures dans votre application :
Listing 6-24

1
2
3
4
5
6

homepage
contact
contact_process
article_show
blog
blog_show

ANY
GET
POST
ANY
ANY
ANY

/
/contact
/contact
/articles/{culture}/{year}/{title}.{_format}
/blog/{page}
/blog/{slug}

Vous pouvez aussi avoir des informations trs spcifiques pour une seule route en incluant le nom de
cette dernire aprs la commande :
Listing 6-25

1 $ php app/console router:debug article_show

New in version 2.1: La commande router:match a t ajoute dans Symfony 2.1

Vous pouvez vrifier quelle route, s'il y en a, correspond un chemin avec la commande router:match :
Listing 6-26

1 $ php app/console router:match /articles/en/2012/article.rss


2 Route "article_show" matches

Gnrer des URLs


Le systme de routage devrait aussi tre utilis pour gnrer des URLs. En ralit, le routage est un
systme bidirectionnel : faire correspondre une URL un contrleur+paramtres et une
route+paramtres une URL. Les mthodes match()1 et generate()2 forment ce systme bidirectionnel. Prenez l'exemple de la route blog_show vue plus haut:
PDF brought to you by
generated on January 14, 2015

Chapter 6: Routage | 71

Listing 6-27

1
2
3
4
5

$params = $router->match('/blog/my-blog-post');
// array('slug' => 'my-blog-post', '_controller' => 'AcmeBlogBundle:Blog:show')
$uri = $router->generate('blog_show', array('slug' => 'my-blog-post'));
// /blog/my-blog-post

Pour gnrer une URL, vous avez besoin de spcifier le nom de la route (par exemple : blog_show) ainsi
que quelconque joker (par exemple : slug = my-blog-post) utilis dans le pattern de cette route. Avec
cette information, n'importe quelle URL peut tre gnre facilement:
Listing 6-28

1 class MainController extends Controller


2 {
3
public function showAction($slug)
4
{
5
// ...
6
7
$url = $this->get('router')->generate('blog_show', array('slug' => 'my-blog-post'));
8
}
9 }

Dans une prochaine section, vous apprendrez comment gnrer des URLs directement depuis les
templates.
Si le front de votre application utilise des requtes AJAX, vous voudrez srement tre capable
de gnrer des URLs en javascript en vous basant sur votre configuration. En utilisant le
FOSJsRoutingBundle3, vous pourrez faire cela :
Listing 6-29

1 var url = Routing.generate('blog_show', { "slug": 'my-blog-post'});

Pour plus d'informations, lisez la documentation du bundle.

Gnrer des URLs Absolues


Par dfaut, le routeur va gnrer des URLs relatives (par exemple : /blog). Pour gnrer une URL absolue,
passez simplement true comme troisime argument de la mthode generate():
Listing 6-30

1 $router->generate('blog_show', array('slug' => 'my-blog-post'), true);


2 // http://www.example.com/blog/my-blog-post

L'host qui est utilis lorsque vous gnrez une URL absolue est celui de l'objet courant Request.
Celui-ci est dtect automatiquement bas sur les informations du serveur fournies par PHP.
Lorsque vous gnrez des URLs absolues pour des scripts excuts depuis la ligne de commande,
vous devrez spcifier manuellement l'host dsir sur l'objet RequestContext:
Listing 6-31

1 $router->getContext()->setHost('www.example.com');

1. http://api.symfony.com/2.6/Symfony/Component/Routing/Router.html#match()
2. http://api.symfony.com/2.6/Symfony/Component/Routing/Router.html#generate()
3. https://github.com/FriendsOfSymfony/FOSJsRoutingBundle

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 72

Gnrer des URLs avec Query Strings


La mthode generate prend un tableau de valeurs jokers pour gnrer l'URI. Mais si vous en passez
d'autres, elles seront ajoutes l'URI en tant que query string
Listing 6-32

1 $router->generate('blog', array('page' => 2, 'category' => 'Symfony'));


2 // /blog/2?category=Symfony

Gnrer des URLs depuis un template


L'endroit principal o vous gnrez une URL est depuis un template lorsque vous crez des liens entre les
pages de votre application. Cela se fait comme on l'a vu auparavant, mais en utilisant une fonction d'aide
(helper) du template :
Listing 6-33

1 <a href="{{ path('blog_show', { 'slug': 'my-blog-post' }) }}">


2
Lire cette entre blog.
3 </a>

Des URLs absolues peuvent aussi tre gnres.


Listing 6-34

1 <a href="{{ url('blog_show', { 'slug': 'my-blog-post' }) }}">


2
Lire cette entre blog.
3 </a>

Rsum
Le routage est un systme permettant de faire correspondre l'URL des requtes entrantes une fonction
contrleur qui devrait tre appele pour traiter la requte. Cela vous permet d'une part de spcifier de
belles URLs et d'autre part de conserver la fonctionnalit de votre application dcouple de ces URLs.
Le routage est un mcanisme bidirectionnel, ce qui signifie qu'il doit aussi tre utilis pour gnrer des
URLs.

En savoir plus grce au Cookbook


Comment forcer les routes toujours utiliser HTTPS ou HTTP

PDF brought to you by


generated on January 14, 2015

Chapter 6: Routage | 73

Chapter 7

Crer et utiliser les templates


Comme vous le savez, le contrleur est responsable de la gestion de toutes les requtes d'une application
Symfony2. En ralit, le contrleur dlgue le plus gros du travail d'autres classes afin que le code puisse
tre test et rutilis. Quand un contrleur a besoin de gnrer du HTML, CSS ou tout autre contenu, il
donne la main au moteur de template. Dans ce chapitre, vous apprendrez comment crire des templates
puissants qui peuvent tre utiliss pour retourner du contenu l'utilisateur, remplir le corps d'emails et
bien d'autres. Vous apprendrez des raccourcis, des mthodes ingnieuses pour tendre les templates et
surtout comment les rutiliser.
L'affichage des templates est abord dans la page contrleur du Book.

Templates
Un template est un simple fichier texte qui peut gnrer n'importe quel format bas sur du texte (HTML,
XML, CSV, LaTeX ...). Le type de template le plus connu est le template PHP - un fichier texte interprt
par PHP qui contient du texte et du code PHP:
Listing 7-1

1 <!DOCTYPE html>
2 <html>
3
<head>
4
<title>Bienvenue sur Symfony !</title>
5
</head>
6
<body>
7
<h1><?php echo $page_title ?></h1>
8
9
<ul id="navigation">
10
<?php foreach ($navigation as $item): ?>
11
<li>
12
<a href="<?php echo $item->getHref() ?>">
13
<?php echo $item->getCaption() ?>
14
</a>

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 74

15
</li>
16
<?php endforeach; ?>
17
</ul>
18
</body>
19 </html>

Mais Symfony2 contient un langage de template encore plus puissant appel Twig1. Twig vous permet
d'crire des templates simples et lisibles qui sont plus comprhensibles par les web designers et, dans bien
des aspects, plus puissants que les templates PHP :
Listing 7-2

1 <!DOCTYPE html>
2 <html>
3
<head>
4
<title>Bienvenue sur Symfony !</title>
5
</head>
6
<body>
7
<h1>{{ page_title }}</h1>
8
9
<ul id="navigation">
10
{% for item in navigation %}
11
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
12
{% endfor %}
13
</ul>
14
</body>
15 </html>

Twig dfinit deux types de syntaxe spciale :


{{ ... }}: Dit quelque chose : crit une variable ou le rsultat d'une expression dans le
template ;
{% ... %}: Fait quelque chose : un tag qui contrle la logique du template ; il est utilis
pour excuter des instructions comme la boucle for par exemple.
Il y a une troisime syntaxe utilise pour les commentaires : {# un commentaire #}. Cette
syntaxe peut tre utilise sur plusieurs lignes comme la syntaxe PHP /* commentaire */ qui est
quivalente.

Twig contient galement des filtres, qui modifient le contenu avant de le rendre. Le filtre suivant met la
variable title en majuscule avant de la rendre :
Listing 7-3

1 {{ title | upper }}

Twig est fourni avec une longue liste de tags2 et de filtres3 qui sont disponibles par dfaut. Vous pouvez
mme ajouter vos propres extensions4 Twig si besoin.
Crer une nouvelle extension Twig est aussi simple que de crer un nouveau service et de le taguer
avec twig.extension tag.

1. http://twig.sensiolabs.org
2. http://twig.sensiolabs.org/doc/tags/index.html
3. http://twig.sensiolabs.org/doc/filters/index.html
4. http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 75

Comme vous le verrez tout au long de la documentation, Twig supporte aussi les fonctions, et de
nouvelles fonctions peuvent tre ajoutes. Par exemple, la fonction suivante utilise le tag standard for et
la fonction cycle pour crire dix balises div en alternant les classes odd et even :
Listing 7-4

1 {% for i in 0..10 %}
2
<div class="{{ cycle(['odd', 'even'], i) }}">
3
<!-- some HTML here -->
4
</div>
5 {% endfor %}

Tout au long de ce chapitre, les exemples de templates seront donns la fois avec Twig et PHP.
Si vous choisissez de ne pas utiliser Twig et que vous le dsactivez, vous devrez implmenter votre
propre gestionnaire d'exceptions via l'vnement kernel.exception.

Pourquoi Twig?
Les templates Twig sont conus pour tre simples et ne traiteront aucun code PHP. De par sa
conception, le systme de template Twig s'occupe de la prsentation, pas de la logique. Plus vous
utiliserez Twig, plus vous apprcierez cette distinction et en bnficierez. Et bien sr, vous serez
ador par tous les web designers.
Twig peut aussi faire des choses que PHP ne pourrait pas faire, comme le contrle d'espaces blancs,
le bac sable, l'chappement de caractres automatique et contextuel et l'inclusion de fonctions et
de filtres personnaliss qui n'affectent que les templates. Twig contient de petites fonctionnalits
qui rendent l'criture de template plus facile et plus concise. Prenez l'exemple suivant, il combine
une boucle avec l'instruction logique if :
Listing 7-5

1 <ul>
2
{% for user in users if user.active %}
3
<li>{{ user.username }}</li>
4
{% else %}
5
<li>Aucun utilisateur trouv.</li>
6
{% endfor %}
7 </ul>

Twig et la mise en cache


Twig est rapide. Chaque template Twig est compil en une classe PHP natif qui est rendue l'excution.
Les classes compiles sont stockes dans le rpertoire app/cache/{environment}/twig (o
{environment} est l'environnement, par exemple dev ou prod) et elles peuvent tre utiles dans certains
cas pour dbugguer. Lisez le chapitre Environnements pour plus d'informations sur les environnements.
Lorsque le mode debug est activ (par exemple en environnement de dev), un template Twig sera
automatiquement recompil chaque fois qu'un changement y sera apport. Cela signifie que durant le
dveloppement, vous pouvez effectuer des modifications dans un template Twig et voir instantanment
les changements sans vous soucier de vider le cache.
Lorsque le mode debug est dsactiv (par exemple en environnement de prod), en revanche, vous devrez
vider le rpertoire de cache Twig afin que le template soit rgnr. Souvenez-vous bien de cela lorsque
vous dploirez votre application.

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 76

L'hritage de template et layouts


Bien souvent, les templates d'un projet partagent des lments communs, comme les enttes, pieds de
page et menus latraux. Dans Symfony2, nous abordons ce problme diffremment : un template peut
tre dcor par un autre. Cela fonctionne exactement comme les classes PHP : l'hritage de template vous
permet de btir un template layout de base qui contient tous les lments communs de votre site et
de dfinir des blocs (comprenez classe PHP avec des mthodes de base ). Un template enfant peut
tendre le template layout et surcharger n'importe lequel de ses blocs (comprenez une sous-classe PHP
qui surcharge certaines mthodes de sa classe parente ).
Tout d'abord, construisez un fichier layout :
Listing 7-6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{% block title %}Test Application{% endblock %}</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
{% endblock %}
</div>
<div id="content">
{% block body %}{% endblock %}
</div>
</body>
</html>

Bien que les explications sur l'hritage de template concernent Twig, la philosophie est la mme
pour les templates PHP.

Ce template dfinit le squelette HTML de base d'un document constitu simplement de deux colonnes.
Dans cette exemple, trois espaces {% block %} sont dfinis (title, sidebar et body). Chacun de
ces blocs peut tre soit surcharg dans un template enfant ou soit conserver leur code d'origine. Ce
template peut aussi tre rendu directement. Dans ce cas, les blocs title, sidebar et body conserveront
simplement les valeurs par dfaut utilises dans ce template.
Un template enfant peut ressembler cela :
Listing 7-7

1
2
3
4
5
6
7
8
9

{# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
{% extends '::base.html.twig' %}
{% block title %}My cool blog posts{% endblock %}
{% block body %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 77

10
{% endfor %}
11 {% endblock %}

Le template parent est identifi grce une chane de caractres particulire (::base.html.twig)
qui indique que ce template se trouve dans le dossier app/Resources/views du projet. Cette
convention de nommage est compltement explique dans Nommage de template et Emplacements.

La cl de l'hritage de template est la balise {% extends %}. Elle indique au moteur de template d'valuer
d'abord le template de base, qui configure le layout et dfinit plusieurs blocs. Le template enfant est
ensuite rendu. Durant ce traitement les blocs parents title et body sont remplacs par ceux de l'enfant.
Dpendant de la valeur de blog_entries, la sortie peut ressembler ceci :
Listing 7-8

1 <!DOCTYPE html>
2 <html>
3
<head>
4
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
<title>Mes billets de blog cools</title>
6
</head>
7
<body>
8
<div id="sidebar">
9
<ul>
10
<li><a href="/">Accueil</a></li>
11
<li><a href="/blog">Blog</a></li>
12
</ul>
13
</div>
14
15
<div id="content">
16
<h2>Mon premier post</h2>
17
<p>Le corps du premier post.</p>
18
19
<h2>Un autre post</h2>
20
<p>Le corps du deuxime post.</p>
21
</div>
22
</body>
23 </html>

Remarquons que comme le template enfant n'a pas dfini le bloc sidebar, la valeur du template parent
est utilise la place. Le contenu d'une balise {% block %} d'un template parent est toujours utilis par
dfaut.
Vous pouvez utiliser autant de niveaux d'hritage que vous souhaitez. Dans la section suivante, un
modle commun d'hritage trois niveaux sera expliqu, ainsi que l'organisation des templates au sein
d'un projet Symfony2.
Quand on travaille avec l'hritage de templates, il est important de garder ces astuces l'esprit :
Si vous utilisez {% extends %} dans un template, alors ce doit tre la premire balise de ce
template.
Plus vous utilisez les balises {% block %} dans les templates, mieux c'est. Souvenez-vous,
les templates enfants ne doivent pas obligatoirement dfinir tous les blocs parents, donc crez
autant de blocs que vous dsirez dans le template de base et attribuez leurs une configuration
par dfaut. Plus vous avez de blocs dans le template de base, plus le layout sera flexible.
Si vous vous retrouvez dupliquer du contenu dans plusieurs templates, cela veut
probablement dire que vous devriez dplacer ce contenu dans un {% block %} d'un template

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 78

parent. Dans certain cas, la meilleure solution peut tre de dplacer le contenu dans un
nouveau template et de l'inclure avec la directive``include`` (voir L'inclusion de Templates).
Si vous avez besoin de rcuprer le contenu d'un bloc d'un template parent, vous pouvez
utiliser la fonction {{ parent() }}. C'est utile si on souhaite complter le contenu du bloc
parent au lieu de le rcrire totalement :

Listing 7-9

1 {% block sidebar %}
2
<h3>Table of Contents</h3>
3
...
4
{{ parent() }}
5 {% endblock %}

Nommage de template et Emplacements


Par dfaut, les templates peuvent se trouver dans deux emplacements diffrents :
app/Resources/views/ : Le dossier views de l'application peut aussi bien contenir le template
de base de l'application (c-a-d le layout de l'application) ou les templates qui surchargent les
templates des bundles (voir La Surcharge de templates de Bundle);
path/to/bundle/Resources/views/ : Chaque bundle place ses templates dans son dossier
Resources/views (et sous dossiers). La plupart des templates rside au sein d'un bundle.
Symfony2 utilise une chane de caractres au format bundle:controller:template pour les templates.
Cela permet plusieurs types de templates, chacun se situant un endroit spcifique :
AcmeBlogBundle:Blog:index.html.twig: Cette syntaxe est utilise pour spcifier un
template pour une page donne. Les trois parties de la chane de caractres, spares par deuxpoints (:), signifient ceci :

AcmeBlogBundle: (bundle) le template se trouve dans le AcmeBlogBundle


(src/Acme/BlogBundle par exemple);
Blog: (controller) indique que le template se trouve dans le sous-rpertoire
Blog de Resources/views;
index.html.twig: (template) le nom rel du fichier est index.html.twig.

En supposant que le AcmeBlogBundle se trouve src/Acme/BlogBundle, le chemin final du


layout serait src/Acme/BlogBundle/Resources/views/Blog/index.html.twig.
AcmeBlogBundle::layout.html.twig: Cette syntaxe fait rfrence un template de base qui
est spcifique au AcmeBlogBundle. Puisque la partie du milieu, controller , est absente
(Blog par exemple), le template se trouve Resources/views/layout.html.twig dans
AcmeBlogBundle.
::base.html.twig: Cette syntaxe fait rfrence un template de base d'une application ou
layout. Remarquez que la chane de caractres commence par deux deux-points (::), ce qui
signifie que les deux parties bundle et controller sont absentes. Ce qui signifie que le template
ne se trouve dans aucun bundle, mais directement dans le rpertoire racine app/Resources/
views/.

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 79

Dans la section La Surcharge de templates de Bundle, vous verrez comment les templates interagissent
avec AcmeBlogBundle. Par exemple, il est possible de surcharger un template en plaant un template
du mme nom dans le rpertoire app/Resources/AcmeBlogBundle/views/. Cela offre la possibilit de
surcharger les templates fournis par n'importe quel vendor bundle.
La syntaxe de nommage des templates doit vous paratre familire - c'est la mme convention de
nommage qui est utilise pour faire rfrence Pattern de Nommage du Contrleur.

Les Suffixes de Template


Le format bundle:controller:template de chaque template spcifie o se situe le fichier template.
Chaque nom de template a aussi deux extensions qui spcifient le format et le moteur (engine) pour le
template.
AcmeBlogBundle:Blog:index.html.twig - format HTML, moteur de template Twig
AcmeBlogBundle:Blog:index.html.php - format HTML, moteur de template PHP
AcmeBlogBundle:Blog:index.css.twig - format CSS, moteur de template Twig
Par dfaut, tout template de Symfony2 peut tre crit soit en Twig ou en PHP, et la dernire partie de
l'extension (.twig ou .php par exemple) spcifie lequel de ces deux moteurs sera utilis. La premire
partie de l'extension (.html, .css par exemple) dsigne le format final du template qui sera gnr.
Contrairement au moteur, qui dtermine comment Symfony2 parsera le template, il s'agit l simplement
une mthode organisationnelle qui est utilise dans le cas o la mme ressource a besoin d'tre rendue en
HTML (index.html.twig), en XML (index.xml.twig), ou tout autre format. Pour plus d'informations,
lisez la section Debugguer.
Les moteurs disponibles peuvent tre configurs et d'autres moteurs peuvent tre ajouts. Voir
Templating Configuration pour plus de dtails.

Balises et Helpers
Vous avez maintenant compris les bases des templates, comment ils sont nomms et comment utiliser
l'hritage de templates. Les parties les plus difficiles sont d'ores et dj derrire vous. Dans cette section,
vous apprendrez utiliser un ensemble d'outils disponibles pour aider raliser les tches les plus
communes avec les templates comme l'inclusion de templates, faire des liens entre des pages et l'inclusion
d'images.
Symfony2 regroupe plusieurs paquets dont plusieurs spcialiss dans les balises et fonctions Twig qui
facilitent le travail du web designer. En PHP, le systme de templates fournit un systme de helper
extensible. Ce systme fournit des proprits utiles dans le contexte des templates.
Nous avons dj vu quelques balises Twig ({% block %} & {% extends %}) ainsi qu'un exemple de
helper PHP ($view['slots']). Apprenons-en un peu plus.

L'inclusion de Templates
Vous voudrez souvent inclure le mme template ou fragment de code dans diffrentes pages. Par
exemple, dans une application avec un espace nouveaux articles , le code du template affichant un
article peut tre utilis sur la page dtaillant l'article, sur une page affichant les articles les plus populaires,
ou dans une liste des derniers articles.

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 80

Quand vous avez besoin de rutiliser une grande partie d'un code PHP, typiquement vous dplacez le
code dans une nouvelle classe PHP ou dans une fonction. La mme chose s'applique aussi aux templates.
En dplaant le code rutilis dans son propre template, il peut tre inclus par tous les autres templates.
D'abord, crez le template que vous souhaiterez rutiliser.
Listing 7-10

1
2
3
4
5
6
7

{# src/Acme/ArticleBundle/Resources/views/Article/articleDetails.html.twig #}
<h2>{{ article.title }}</h2>
<h3 class="byline">by {{ article.authorName }}</h3>
<p>
{{ article.body }}
</p>

L'inclusion de ce template dans tout autre template est simple :


Listing 7-11

1
2
3
4
5
6
7
8
9
10

{# src/Acme/ArticleBundle/Resources/views/Article/list.html.twig #}
{% extends 'AcmeArticleBundle::layout.html.twig' %}
{% block body %}
<h1>Recent Articles<h1>
{% for article in articles %}
{{ include('AcmeArticleBundle:Article:articleDetails.html.twig', {'article':
article}) }}
{% endfor %}
{% endblock %}

Le template est inclus via l'utilisation de la balise {{ include() }}. Remarquons que le nom du template
suit la mme convention habituelle. Le template articleDetails.html.twig utilise une variable
article. Elle est reue par le template list.html.twig en second paramtre de la fonction include().
La syntaxe {'article': article} est la syntaxe standard de Twig pour les tables de hachage
(hash maps) (c-a-d un tableau cl-valeurs). Si vous souhaitez passer plusieurs elements, cela
ressemblera ceci : {'foo': foo, 'bar': bar}.

Contrleurs imbriqus
Dans certains cas, vous aurez besoin d'inclure plus qu'un simple template. Supposons que vous avez un
menu latral dans votre layout qui contient les trois articles les plus rcents. La rcupration des trois
articles les plus rcents peut ncessiter l'inclusion d'une requte vers une base de donnes et de raliser
d'autres oprations logiques qui ne peuvent pas tre effectues dans un template.
La solution consiste simplement imbriquer les rsultats d'un contrleur dans un template. Dans un
premier temps, crez un contrleur qui retourne un certain nombre d'articles rcents:
Listing 7-12

1
2
3
4
5
6
7
8
9
10

// src/Acme/ArticleBundle/Controller/ArticleController.php
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// un appel en base de donnes ou n'importe quoi qui retourne les "$max" plus
rcents articles
$articles = ...;

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 81

11
return $this->render('AcmeArticleBundle:Article:recentList.html.twig',
12 array('articles' => $articles));
}
}

Le template recentList est simplement le suivant :


Listing 7-13

1 {# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
2 {% for article in articles %}
3
<a href="/article/{{ article.slug }}">
4
{{ article.title }}
5
</a>
6 {% endfor %}

Notez que dans l'exemple de cet article, les URLs sont codes en dur (/article/*slug* par
exemple). Ce n'est pas une bonne pratique. Dans la section suivante, vous apprendrez comment le
faire correctement.

Pour inclure le contrleur, vous avez besoin de faire rfrence ce dernier en utilisant la chane de
caractres standard pour les contrleurs (c-a-d bundle:controller:action) :
Listing 7-14

1
2
3
4
5
6

{# app/Resources/views/base.html.twig #}
...
<div id="sidebar">
{{ render(controller('AcmeArticleBundle:Article:recentArticles', { 'max': 3 })) }}
</div>

A chaque fois que vous pensez avoir besoin d'une variable ou de quelques informations auxquelles vous
n'avez pas accs partir d'un template, penser rendre un contrleur. Les contrleurs sont rapides
l'excution et favorisent une bonne organisation et rutilisabilit du code.

Contenu asynchrone avec hinclude.js


New in version 2.1: hinclude.js support was added in Symfony 2.1

Les contrleurs peuvent tre imbriqus de faon asynchrone avec la bibliothque javascript hinclude.js5.
Comme le contenu imbriqu vient d'une autre page (un d'un autre contrleur), Symfony2 utilise le helper
standard render pour configurer les tags hinclude:
Listing 7-15

1 {% render '...:news' with {}, {'standalone': 'js'} %}

hinclude.js6 doit tre inclus dans votre page pour fonctionner.

5. http://mnot.github.com/hinclude/
6. http://mnot.github.com/hinclude/

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 82

Le contenu par dfaut (pendant le chargement ou si javascript n'est pas activ) peut tre dfini de manire
globale dans la configuration de votre application :
Listing 7-16

1 # app/config/config.yml
2 framework:
3
# ...
4
templating:
5
hinclude_default_template: AcmeDemoBundle::hinclude.html.twig

Liens vers des Pages


La cration de liens vers d'autres pages de votre projet est l'opration la plus commune qui soit dans un
template. Au lieu de coder en dur les URLs dans les templates, utilisez la fonction path de Twig (ou le
helper router en PHP) pour gnrer les URLs bases sur la configuration des routes. Plus tard, si vous
dsirez modifier l'URL d'une page particulire, tout ce que vous avez besoin de faire c'est changer la
configuration des routes; les templates gnreront automatiquement la nouvelle URL.
Dans un premier temps, configurons le lien vers la page _welcome qui est accessible via la
configuration de route suivante :
Listing 7-17

1 _welcome:
2
pattern: /
3
defaults: { _controller: AcmeDemoBundle:Welcome:index }

Pour faire un lien vers cette page, utilisons simplement la fonction path de Twig en faisant rfrence
cette route :
Listing 7-18

1 <a href="{{ path('_welcome') }}">Home</a>

Comme prvu, ceci gnrera l'URL /. Voyons comment cela fonctionne avec des routes plus compliques
:
Listing 7-19

1 article_show:
2
pattern: /article/{slug}
3
defaults: { _controller: AcmeArticleBundle:Article:show }

Dans ce cas, vous devrez spcifier le nom de route (article_show) et une valeur pour le paramtre
{slug}. En utilisant cette route, revoyons le template recentList de la section prcdente, et faisons les
liens vers les articles correctement :
Listing 7-20

1 {# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
2 {% for article in articles %}
3
<a href="{{ path('article_show', { 'slug': article.slug }) }}">
4
{{ article.title }}
5
</a>
6 {% endfor %}

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 83

Vous pouvez aussi gnrer l'URL absolue en utilisant la fonction url de Twig :
Listing 7-21

1 <a href="{{ url('_welcome') }}">Home</a>

La mme chose peut tre ralise dans les templates en PHP en passant un troisime argument la
mthode generate() :
Listing 7-22

1 <a href="<?php echo $view['router']->generate('_welcome', array(), true) ?>">Home</a>

Liens vers des Fichiers


Les templates font aussi trs souvent rfrence des images, du Javascript, des feuilles de style et d'autres
fichiers. Bien sr vous pouvez coder en dur le chemin vers ces fichiers (/images/logo.png par exemple),
mais Symfony2 fournit une faon de faire plus souple via la fonction asset de Twig :
Listing 7-23

1 <img src="{{ asset('images/logo.png') }}" alt="Symfony!" />


2
3 <link href="{{ asset('css/blog.css') }}" rel="stylesheet" type="text/css" />

Le principal objectif de la fonction asset est de rendre votre application plus portable. Si votre
application se trouve la racine de votre hte (http://example.com7 par exemple), alors les chemins
se retourn sera /images/logo.png. Mais si votre application se trouve dans un sous rpertoire
(http://example.com/my_app8 par exemple), chaque chemin vers les fichiers sera alors gnr avec le
sous rpertoire (/my_app/images/logo.png par exemple). La fonction asset fait attention cela en
dterminant comment votre application est utilise et en gnrant les chemins corrects.
De plus, si vous utilisez la fonction asset, Symfony peut automatiquement ajouter une chane de
caractres afin de garantir que la ressource statique mise jour ne sera pas mise en cache lors de son
dploiement. Par exemple, /images/logo.png pourrait ressembler /images/logo.png?v2. Pour plus
d'informations, lisez la documentation de l'option de configuration assets_version.

L'inclusion de Feuilles de style et de Javascripts avec Twig


Aucun site n'est complet sans inclure des fichiers Javascript et des feuilles de styles. Dans Symfony,
l'inclusion de ces fichiers est gre d'une faon lgante en conservant les avantages du mcanisme
d'hritage de templates de Symfony.
Cette section vous apprendra la philosophie qui existe derrire l'inclusion de feuilles de style
et de fichiers Javascript dans Symfony. Symfony contient aussi une autre bibliothque, appele
Assetic, qui suit la mme philosophie, mais vous permet de faire des choses plus intressantes
avec ces fichiers. Pour plus d'informations sur le sujet voir Comment utiliser Assetic pour grer vos
ressources.

Commenons par ajouter deux blocs notre template de base qui incluront deux fichiers : l'un s'appelle
stylesheet et est inclus dans la balise head, et l'autre s'appelle javascript et est inclus juste avant que la
balise body ne se referme. Ces blocs contiendront toutes les feuilles de style et tous les fichiers javascript
dont vous aurez besoin pour votre site :

7. http://example.com
8. http://example.com/my_app

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 84

Listing 7-24

1 {# 'app/Resources/views/base.html.twig' #}
2 <html>
3
<head>
4
{# ... #}
5
6
{% block stylesheets %}
7
<link href="{{ asset('/css/main.css') }}" type="text/css" rel="stylesheet" />
8
{% endblock %}
9
</head>
10
<body>
11
{# ... #}
12
13
{% block javascripts %}
14
<script src="{{ asset('/js/main.js') }}" type="text/javascript"></script>
15
{% endblock %}
16
</body>
17 </html>

C'est assez simple. Mais comment faire si vous avez besoin d'inclure une feuille de style supplmentaire
ou un autre fichier javascript partir d'un template enfant ? Par exemple, supposons que vous avez une
page contact et que vous avez besoin d'inclure une feuille de style contact.css uniquement sur cette
page. Au sein du template de la page contact, faites comme ce qui suit :
Listing 7-25

1
2
3
4
5
6
7
8
9
10

{# src/Acme/DemoBundle/Resources/views/Contact/contact.html.twig #}
{% extends '::base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link href="{{ asset('/css/contact.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}

{# ... #}

Dans le template enfant, nous surchargeons simplement le bloc stylesheets en ajoutant une nouvelle
balise de feuille de style dans ce bloc. Bien sr, puisque nous voulons ajouter du contenu au bloc parent
(et non le remplacer), nous devons utiliser la fonction Twig parent() pour inclure le bloc stylesheets
du template de base.
Vous pouvez aussi inclure des ressources situes dans le dossier Resources/public de vos bundles. Vous
devrez lancer la commande php app/console assets:install target [--symlink] pour placer les
fichiers dans le bon rpertoire ("web" par dfaut)
Listing 7-26

1 <link href="{{ asset('bundles/acmedemo/css/contact.css') }}" type="text/css"


rel="stylesheet" />

Le rsultat final est une page qui inclut la fois la feuille de style main.css et contact.css.

Configuration et Utilisation du Service templating


Le coeur du systme de template dans Symfony2 est le moteur de template (Engine). Cet objet spcial
est responsable de rendre des templates et de retourner leur contenu. Quand vous effectuez le rendu d'un
template travers un contrleur par exemple, vous utilisez en effet le service de moteur de template. Par
exemple:

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 85

Listing 7-27

1 return $this->render('AcmeArticleBundle:Article:index.html.twig');

est quivalent :
Listing 7-28

1 $engine = $this->container->get('templating');
2 $content = $engine->render('AcmeArticleBundle:Article:index.html.twig');
3
4 return $response = new Response($content);

Le moteur de template (ou service ) est prconfigur pour fonctionner automatiquement dans
Symfony2. Il peut, bien sr, tre configur grce au fichier de configuration de l'application :
Listing 7-29

1 # app/config/config.yml
2 framework:
3
# ...
4
templating: { engines: ['twig'] }

Plusieurs options de configuration sont disponibles et sont dtailles dans le Configuration Appendix.
Le moteur twig est ncessaire pour utiliser un webprofiler (de mme que beaucoup de bundles
tiers).

La Surcharge de templates de Bundle


La communaut Symfony2 est fire de crer et de maintenir des bundles de haute qualit (voir
KnpBundles.com9) concernant un grand nombre de fonctionnalits. Une fois que vous utilisez un tel
bundle, vous aimeriez srement surcharger et personnaliser un ou plusieurs de ses templates.
Supposons que vous utilisiez un imaginaire AcmeBlogBundle open source dans votre projet (dans le
rpertoire src/Acme/blogBundle par exemple). Mme si vous tes trs content de ce bundle, vous
voudriez probablement surcharger la page liste du blog pour la personnaliser et l'adapter spcialement
votre application. En cherchant un peu dans le contrleur Blog du AcmeBlogBundle, vous trouvez ceci
:
Listing 7-30

1 public function indexAction()


2 {
3
$blogs = // logique qui rcupre les blogs
4
5
$this->render('AcmeBlogBundle:Blog:index.html.twig', array('blogs' => $blogs));
6 }

Quand le AcmeBlogBundle:Blog:index.html.twig est rendu, Symfony2 regarde en fait dans deux


emplacements pour le template :
1. app/Resources/AcmeBlogBundle/views/Blog/index.html.twig
2. src/Acme/BlogBundle/Resources/views/Blog/index.html.twig
Pour surcharger le template du bundle, il suffit de copier le template index.html.twig du bundle
vers app/Resources/AcmeBlogBundle/views/Blog/index.html.twig (le rpertoire app/Resources/
AcmeBlogBundle n'existe pas, nous vous laissons le soin de le crer). Vous tes maintenant mme de
personnaliser le template.
9. http://knpbundles.com

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 86

Si vous ajoutez un template un nouvel endroit, vous pourriez avoir besoin de vider votre cache
(php app/console cache:clear), mme si vous tes en mode debug.

Cette logique s'applique aussi au template layout. Supposons maintenant que chaque template dans
AcmeBlogBundle hrite d'un template de base appel AcmeBlogBundle::layout.html.twig. Comme
prcdemment, Symfony2 regardera dans les deux emplacements suivants :
1. app/Resources/AcmeBlogBundle/views/layout.html.twig
2. src/Acme/BlogBundle/Resources/views/layout.html.twig
Encore une fois, pour surcharger le template, il suffit de le copier du bundle vers app/Resources/
AcmeBlogBundle/views/layout.html.twig. Vous tes maintenant libre de personnaliser cette copie
comme il vous plaira.
Si vous prenez du recul, vous vous apercevrez que Symfony2 commence toujours par regarder dans le
rpertoire app/Resources/{BUNDLE_NAME}/views/ pour un template. Si le template n'existe pas l, il
continue sa recherche dans le rpertoire Resources/views du bundle lui-mme. Ce qui signifie que tous
les templates d'un bundle peuvent tre surchargs condition de les placer dans le bon sous-rpertoire
de app/Resources.
Vous pouvez galement surcharger les templates provenant d'un bundle grce l'hritage de
bundle. Pour plus d'informations, voir Comment utiliser l'hritage de bundle pour surcharger
certaines parties d'un bundle.

La Surcharge des Core Templates


Puisque le framework Symfony2 lui-mme est juste un bundle, les templates du noyau peuvent tre
surchargs de la mme faon. Par exemple, le bundle noyau TwigBundle contient un certain nombre de
templates relatifs aux exceptions et aux erreurs qui peuvent tre surchargs en copiant chacun
d'eux du rpertoire Ressources/views/Exception du TwigBundle vers, vous l'avez devin, le rpertoire
app/Resources/TwigBundle/views/Exception.

Trois niveaux d'hritages


Une faon commune d'utiliser l'hritage est d'utiliser l'approche trois niveaux. Cette mthode
fonctionne parfaitement avec trois diffrents types de templates dtaills :
Crez un fichier app/Resources/views/base.html.twig qui contient le principal layout pour
votre application (comme dans l'exemple prcdent). En interne, ce template est appel
::base.html.twig;
Crez un template pour chaque section de votre site. Par exemple, un AcmeBlogBundle
devrait avoir un template appel AcmeBlogBundle::layout.html.twig qui contient
uniquement les lments de section spcifiques au blog;

Listing 7-31

1 {# src/Acme/BlogBundle/Resources/views/layout.html.twig #}
2 {% extends '::base.html.twig' %}
3
4 {% block body %}
5
<h1>Blog Application</h1>
6

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 87

7
{% block content %}{% endblock %}
8 {% endblock %}

Crez un template individuel pour chaque page et faites en sorte que chacun tende (extend)
le template appropri la section. Par exemple, la page index sera appele peu de chose
prs AcmeBlogBundle:Blog:index.html.twig et listera les billets du blog.

Listing 7-32

1
2
3
4
5
6
7
8
9

{# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
{% extends 'AcmeBlogBundle::layout.html.twig' %}
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

Remarquons que ce template tend la section (AcmeBlogBundle::layout.html.twig) qui son tour


tend le layout de base de l'application (::base.html.twig). C'est le modle commun d'hritage trois
niveaux.
Quand vous construisez votre application, vous pouvez choisir de suivre cette mthode ou simplement
faire que chaque template de page tende le layout de l'application directement ({% extends
'::base.html.twig' %} par exemple). Le modle des trois templates est une meilleure pratique, utilise
par les bundles vendor. De ce fait, le layout d'un bundle peut facilement tre surcharg pour tendre
proprement le layout de base de votre application.

L'chappement
Lors de la gnration HTML d'un template, il y a toujours un risque qu'une variable du template affiche
du code HTML non dsir ou du code dangereux ct client. Le rsultat est que le contenu dynamique
peut casser le code HTML de la page rsultante ou permettre un utilisateur malicieux de raliser une
attaque Cross Site Scripting10 (XSS). Considrons cet exemple classique :
Listing 7-33

1 Hello {{ name }}

Imaginons que l'utilisateur ait rentr le code suivant comme son nom :
Listing 7-34

1 <script>alert('hello!')</script>

Sans chappement du rendu, le template rsultant provoquera l'affichage d'une bote d'alert Javascript :
Listing 7-35

1 Hello <script>alert('hello!')</script>

10. http://en.wikipedia.org/wiki/Cross-site_scripting

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 88

Et bien que cela semble inoffensif, si un utilisateur peut aller aussi loin, ce mme utilisateur peut aussi
crire un Javascript qui ralise des actions malicieuses dans un espace scuris d'un inconnu et lgitime
utilisateur.
La rponse ce problme est l'chappement (output escaping). En activant l'chappement, le mme
template sera rendu de faon inoffensive, et affichera littralement la balise script l'cran :
Listing 7-36

1 Hello &lt;script&gt;alert(&#39;helloe&#39;)&lt;/script&gt;

Les systmes de template Twig et PHP abordent le problme diffremment. Si vous utilisez Twig,
l'chappement est activ par dfaut et vous tes protg. En PHP, l'chappement n'est pas automatique,
ce qui signifie que vous aurez besoin d'chapper manuellement l o c'est ncessaire.

L'chappement avec Twig


Si vous utilisez les templates Twig, alors l'chappement est activ par dfaut. Ce qui signifie que vous
tes protg immdiatement des consquences non intentionnelles du code soumis par l'utilisateur. Par
dfaut, l'chappement suppose que le contenu est bien chapp pour un affichage HTML.
Dans certains cas, vous aurez besoin de dsactiver l'chappement de la sortie lors du rendu d'une variable
qui est sure et qui contient des dcorations qui ne doivent pas tre chappes. Supposons que des
utilisateurs administrateurs sont capables dcrire des articles qui contiennent du code HTML. Par dfaut,
Twig chappera le corps de l'article. Pour le rendre normalement, il suffit d'ajouter le filtre raw : {{
article.body | raw }}.
Vous pouvez aussi dsactiver l'chappement au sein d'un {% block %} ou pour un template entier. Pour
plus d'informations, voir Output Escaping11 dans la documentation de Twig.

L'chappement en PHP
L'chappement n'est pas automatique lorsque vous utilisez des templates PHP. Ce qui signifie que,
moins que vous ne choisissiez explicitement d'chapper une variable, vous n'tes pas protg. Pour
utiliser l'chappement, utilisez la mthode spciale escape() de view : :
Listing 7-37

1 Hello <?php echo $view->escape($name) ?>

Par dfaut, la mthode escape() suppose que la variable est rendue dans un contexte HTML (et donc
que la variable est chappe pour tre sans danger pour l'HTML). Le second argument vous permet de
changer de contexte. Par exemple, pour afficher quelque chose dans une chane de caractres JavaScript,
utilisez le context js :
Listing 7-38

1 var myMsg = 'Hello <?php echo $view->escape($name, 'js') ?>';

Debugguer
New in version 2.0.9: Cette fonctionnalit est disponible depuis Twig 1.5.x, qui a t fourni pour
la premire fois avec Symfony 2.0.9.

11. http://twig.sensiolabs.org/doc/api.html#escaper-extension

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 89

Lorsque vous utilisez PHP, vous pouvez utiliser var_dump() si vous avez besoin de trouver rapidement la
valeur d'une variable. C'est utile, par exemple, dans un contrleur. La mme chose peut tre faite lorsque
vous utilisez Twig en utilisant l'extension de debug. Elle a besoin d'tre active dans la configuration :
Listing 7-39

1 # app/config/config.yml
2 services:
3
acme_hello.twig.extension.debug:
4
class:
Twig_Extension_Debug
5
tags:
6
- { name: 'twig.extension' }

Les paramtres de template peuvent tre affichs en utilisant la fonction dump :


Listing 7-40

1
2
3
4
5
6
7
8

{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{{ dump(articles) }}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}

Les variables ne seront affiches que si l'option debug de Twig (dans config.yml) est true. Cela signifie
que par dfaut, les variables seront affiches en environnement de dev mais pas en environnement de
prod.

Vrification de la syntaxe
New in version 2.1: La commande twig:lint a t ajoute dans Symfony 2.1

Vous pouvez vrifier les ventuelles erreurs de syntaxe dans les templates Twig en utilisant la commande
twig:lint :
Listing 7-41

1
2
3
4
5
6
7
8

# Vous pouvez vrifier par nom de fichier :


$ php app/console twig:lint src/Acme/ArticleBundle/Resources/views/Article/
recentList.html.twig
# ou par rpertoire :
$ php app/console twig:lint src/Acme/ArticleBundle/Resources/views
# ou en utilisant le nom du bundle :
$ php app/console twig:lint @AcmeArticleBundle

Les Formats de Template


Les templates sont une faon gnrique de rendre un contenu dans n'importe quel format. Et bien que
dans la plupart des cas vous utiliserez les templates pour rendre du contenu HTML, un template peut
tout aussi facilement gnrer du JavaScript, du CSS, du XML ou tout autre format dont vous pouvez
rver.

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 90

Par exemple, la mme ressource est souvent rendue dans plusieurs formats diffrents. Pour rendre la
page index d'un article en XML, incluez simplement le format dans le nom du template :
nom du template XML: AcmeArticleBundle:Article:index.xml.twig
nom de fichier du template XML: index.xml.twig
En ralit, ce n'est rien de plus qu'une convention de nommage et le template n'est pas rendu
diffremment en se basant sur ce format.
Dans beaucoup de cas, vous pourriez vouloir autoriser un simple contrleur rendre plusieurs formats
en se basant sur le request format . Pour cette raison, un pattern commun est de procder comme cela:
Listing 7-42

1 public function indexAction()


2 {
3
$format = $this->getRequest()->getRequestFormat();
4
5
return $this->render('AcmeBlogBundle:Blog:index.'.$format.'.twig');
6 }

Le getRequestFormat sur l'objet Request retourne par dfaut html, mais peut aussi retourner n'importe
quel autre format bas sur le format demand par l'utilisateur. Le format demand est le plus souvent gr
par le systme de route, o une route peut tre configure telle que /contact dfinit le format demand
html alors que /contact.xml dfinit le format xml. Pour plus d'informations, voir l'Exemple avanc
du chapitre routing.
Pour crer des liens qui incluent le paramtre de format, incluez une cl _format dans le tableau de
paramtres :
Listing 7-43

1 <a href="{{ path('article_show', {'id': 123, '_format': 'pdf'}) }}">


2
PDF Version
3 </a>

Rflexions Finales
Le moteur de template dans Symfony est un outil puissant qui peut tre utilis chaque fois que vous
avez besoin de gnrer du contenu de reprsentation en HTML, XML ou tout autre format. Et bien que
les templates soient un moyen commun de gnrer du contenu dans un contrleur, leur utilisation n'est
pas systmatique. L'objet Response retourn par un contrleur peut tre cr avec ou sans utilisation de
template:
Listing 7-44

1
2
3
4
5

// cration d'un objet Response qui contient le rendu d'un template


$response = $this->render('AcmeArticleBundle:Article:index.html.twig');
// cration d'un objet Response qui contient un texte simple
$response = new Response('response content');

Le moteur de templates de Symfony est trs flexible et deux outils de restitution sont disponibles par
dfaut : les traditionnels templates PHP et les lgants et puissants templates Twig. Ils supportent tous les
deux une hirarchie des template et sont fournis avec un ensemble riche de fonctions capables de raliser
la plupart des tches.
Dans l'ensemble, le systme de templates doit tre pens comme tant un outil puissant qui est votre
disposition. Dans certains cas, vous n'aurez pas besoin de rendre un template, et dans Symfony2, c'est
tout fait envisageable.

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 91

En savoir plus grce au Cookbook


Comment utiliser PHP plutt que Twig dans les templates
Comment personnaliser les pages d'erreur
Comment crire une Extension Twig personnalise

PDF brought to you by


generated on January 14, 2015

Chapter 7: Crer et utiliser les templates | 92

Chapter 8

Doctrine et les bases de donnes


L'une des tches les plus courantes et difficiles pour toute application consiste lire et persister des
informations dans une base de donnes. Heureusement, Symfony intgre Doctrine1, une bibliothque
dont le seul but est de vous fournir des outils puissants afin de vous faciliter la tche. Dans ce chapitre,
vous apprendrez les bases de la philosophie de Doctrine et verrez quel point il peut tre facile de
travailler avec une base de donnes.
Doctrine est totalement dcoupl de Symfony et son utilisation est optionnelle. Ce chapitre est
entirement consacr l'ORM Doctrine, dont l'objectif est de mapper vos objets avec une base de
donnes relationnelle (comme MySQL, PostGresSQL ou Microsoft SQL). Si vous prfrez utiliser
des requtes SQL brutes, c'est facile, et expliqu dans l'article Comment utiliser la couche DBAL
de Doctrine du cookbook
Vous pouvez aussi persister vos donnes l'aide de MongoDB2 en utilisant la bibliothque ODM
de Doctrine. Pour plus d'informations, lisez la documentation DoctrineMongoDBBundle .

Un simple exemple : un produit


La manire la plus facile de comprendre comment Doctrine fonctionne est de le voir en action. Dans cette
section, vous allez configurer votre base de donnes, crer un objet Product, le faire persister dans la base
de donnes et le rcuprer.

Coder les exemples en mme temps


Si vous souhaitez suivre les exemples au fur et mesure, crer un AcmeStoreBundle l'aide de la
commande :
Listing 8-1

1 $ php app/console generate:bundle --namespace=Acme/StoreBundle

1. http://www.doctrine-project.org/
2. http://www.mongodb.org/

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 93

Configurer la base de donnes


Avant que vous ne soyez rellement prt, vous devez configurer les paramtres de connexion votre
base de donnes. Par convention, ces paramtres sont habituellement placs dans le fichier app/config/
parameters.yml :
Listing 8-2

1 # app/config/parameters.yml
2 parameters:
3
database_driver:
pdo_mysql
4
database_host:
localhost
5
database_name:
test_project
6
database_user:
root
7
database_password: password
8
9 # ...

Dfinir la configuration dans parameters.yml est juste une convention. Les paramtres dfinis
dans ce fichier sont rfrencs dans le fichier de configuration principal au moment de configurer
Doctrine :
Listing 8-3

1 # app/config/config.yml
2 doctrine:
3
dbal:
4
driver:
"%database_driver%"
5
host:
"%database_host%"
6
dbname:
"%database_name%"
7
user:
"%database_user%"
8
password: "%database_password%"

En stockant ces paramtres de connexion dans un fichier spar, vous pouvez facilement garder
une version diffrente de ce fichier sur chaque serveur. Vous pouvez aussi stocker la configuration
de la base de donnes (ou n'importe quelle information sensible) en dehors de votre projet, par
exemple dans votre configuration Apache. Pour plus d'informations, consultez l'article Comment
configurer les paramtres externes dans le conteneur de services.

Maintenant que Doctrine connat vos paramtres de connexion, vous pouvez lui demander de crer votre
base de donnes :
Listing 8-4

1 $ php app/console doctrine:database:create

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 94

Configurer la base de donnes en UTF8


Une erreur que font mme les dveloppeurs les plus chevronns est d'oublier de dfinir un jeu de
caractres (charset) et une collation par dfaut sur leurs bases de donnes. Ils se retrouvent alors
avec une collation de type latin qui est la valeur par dfaut de la plupart des bases de donnes.
Mme s'ils pourraient penser le faire la toute premire fois, ils oublient que tout serait refaire
aprs avoir lanc des commandes telles que :
Listing 8-5

1 $ php app/console doctrine:database:drop --force


2 $ php app/console doctrine:database:create

Il n'y a aucune manire de configurer ces paramtres par dfaut dans Doctrine, puisque Doctrine
essaye d'tre aussi agnostique que possible en terme de configuration. Un moyen de rsoudre ce
problme est de configurer les valeurs par dfaut au niveau du serveur.
Dfinir UTF8 par dfaut pour MySQL est aussi simple que d'ajouter ces quelques lignes votre
fichier de configuration (typiquement my.cnf) :
Listing 8-6

1 [mysqld]
2 collation-server = utf8_general_ci
3 character-set-server = utf8

Si vous voulez utiliser SQLite comme base de donnes, vous devrez dfinir le chemin du fichier qui
stockera votre base de donnes :
Listing 8-7

1 # app/config/config.yml
2 doctrine:
3
dbal:
4
driver: pdo_sqlite
5
path: "%kernel.root_dir%/sqlite.db"
6
charset: UTF8

Crer une classe entit


Supposons que vous criez une application affichant des produits. Sans mme penser Doctrine ou
votre base de donnes, vous savez dj que vous aurez besoin d'un objet Product reprsentant ces
derniers. Crez cette classe dans le rpertoire Entity de votre bundle AcmeStoreBundle:
Listing 8-8

1
2
3
4
5
6
7
8
9
10
11

// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
class Product
{
protected $name;
protected $price;
protected $description;
}

Cette classe - souvent appele une entit , ce qui veut dire une classe basique qui contient des donnes
- est simple et remplit les besoins mtiers des produits dans votre application. Cette classe ne peut pas
encore tre persiste dans une base de donnes - c'est juste une simple classe PHP.
PDF brought to you by
generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 95

Une fois que vous connaissez les concepts derrire Doctrine, vous pouvez l'utiliser pour crer ces
classes entit pour vous :
Listing 8-9

1 $ php app/console doctrine:generate:entity \


2
--entity="AcmeStoreBundle:Product" \
3
--fields="name:string(255) price:float description:text"

Ajouter des informations de mapping


Doctrine vous permet de travailler avec des bases de donnes d'une manire beaucoup plus intressante
que de transformer des lignes en tableaux en vous basant sur des colonnes. Au lieu de a, Doctrine vous
permet de persister des objets entiers dans votre base de donnes et rcuprer ces objets depuis votre
base de donnes. Ce systme fonctionne en associant vos classes PHP avec des tables de votre base, et les
proprits de ces classes PHP avec des colonnes de la table, c'est ce que l'on appelle le mapping :

Pour que Doctrine soit capable de faire a, vous n'avez qu' crer des mtadonnes , ou configurations
qui expliquent Doctrine exactement comment la classe Product et ses proprits doivent tre mappes
avec la base de donnes. Ces mtadonnes peuvent tre spcifies dans de nombreux formats incluant le
YAML, XML ou directement dans la classe Product avec les annotations :
Listing 8-10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="product")
*/
class Product
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=100)
*/
protected $name;

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 96

24
25
26
27
28
29
30
31
32
33 }

/**
* @ORM\Column(type="decimal", scale=2)
*/
protected $price;
/**
* @ORM\Column(type="text")
*/
protected $description;

Un bundle ne peut accepter qu'un format de dfinition des mtadonnes. Par exemple, il n'est pas
possible de mlanger des dfinitions au format YAML avec des entits annotes dans les classes
PHP.

Le nom de la table est optionnel et, s'il est omis, sera dtermin automatiquement en se basant sur
le nom de la classe de l'entit.

Doctrine vous permet de choisir parmi une trs grande varit de types de champs chacun avec ses
propres options. Pour obtenir des informations sur les types de champs disponibles, reportez vous la
section Rfrence des types de champs de Doctrine.
Vous pouvez aussi regarder la documentation sur les Bases du Mapping3 de Doctrine pour avoir tout les
dtails propos des informations de mapping. Si vous utilisez les annotations, vous devrez prfixer toutes les
annotations avec ORM\ (ex: ORM\Column(..)), ce qui n'est pas montr dans la documentation de Doctrine.
Vous devez aussi inclure le morceau de code : use Doctrine\ORM\Mapping as ORM;, qui importe le prfixe
ORM pour les annotations.

Faites bien attention que vos noms de classe et de proprits ne soient pas mapps avec des motscls SQL (comme group ou user). Par exemple, si le nom de la classe de votre entit est Group
alors, par dfaut, le nom de la table correspondante sera group, ce qui causera des problmes SQL
avec certains moteurs. Lisez la documentation sur les Mots-cl SQL rservs4 de Doctrine pour
savoir comment chapper ces noms. Alternativement, si vous tes libre de choisir votre schma de
base de donnes, vous pouvez simplement utiliser un autre nom de table ou de colonne. Lisez les
documentations de Doctrine Classes persistantes5 et Mapping de proprits6.

3.
4.
5.
6.

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/basic-mapping.html#persistent-classes
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/basic-mapping.html#property-mapping

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 97

Si vous utilisez une autre bibliothque ou un programme (comme Doxygen) qui utilise les
annotations, vous devrez placer une annotation @IgnoreAnnotation sur votre classe pour indiquer
Symfony quelles annotations il devra ignorer.
Par exemple, pour empcher l'annotation @fn de lancer une exception, ajouter le code suivant:
Listing 8-11

1 /**
2 * @IgnoreAnnotation("fn")
3 */
4 class Product
5 // ...

Gnrer les getters et setters


Mme si Doctrine sait maintenant comment persister un objet Product dans la base de donnes, la classe
elle-mme n'est pas encore trs utile. Comme Product est juste une simple classe PHP, vous devez crer
des getters et des setters (ex: getName(), setName()) pour pouvoir accder ces proprits (car elles sont
protected). Heureusement, Doctrine peut faire a pour vous en lanant la commande :
Listing 8-12

1 $ php app/console doctrine:generate:entities Acme/StoreBundle/Entity/Product

Cette commande s'assure que tous les getters et les setters sont gnrs pour la classe Product. C'est une
commande sure - vous pouvez la lancer encore et encore : elle ne gnrera que les getters et les setters qui
n'existent pas (c--d qu'elle ne remplace pas les mthodes existantes)
Gardez en tte que le gnrateur d'entits de Doctrine ne produit que de simples getters/setters.
Vous devrez vrifier les entits gnres et ajuster la logique des getters/setters selon vos propres
besoins.

Un peu plus sur doctrine:generate:entities


Avec la commande doctrine:generate:entities, vous pouvez :

gnrer les getters et setters;


gnrer les classes repository configures avec les annotations
@ORM\Entity(repositoryClass="...");
gnrer les constructeurs appropris pour les relations 1:n et n:m.

La commande doctrine:generate:entities fait une sauvegarde de Product.php appele


Product.php~. Dans certains cas, la prsence de ce fichier peut crer l'erreur Cannot redeclare
class . Vous pouvez supprimer ce fichier en toute scurit. Vous pouvez aussi utiliser l'option "-no-backup" pour viter de gnrer ces fichiers de sauvegarde.
Notez bien que vous n'avez pas besoin d'utiliser cette commande. Doctrine ne se base pas sur
la gnration de code. Comme les classes PHP classiques, vous devez juste vous assurer que vos
proprits protected/private ont bien leurs mthodes getter et setter associes. Comme c'est une
tche rcurrente faire avec Doctrine, cette commande a t cre.

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 98

Vous pouvez galement gnrer toutes les entits connues (c--d toute classe PHP qui contient des
informations de mapping Doctrine) d'un bundle ou d'un namespace :
Listing 8-13

1 $ php app/console doctrine:generate:entities AcmeStoreBundle


2 $ php app/console doctrine:generate:entities Acme

Doctrine se moque que vos proprits soient protected ou private, ou mme que vous ayez un
getter ou un setter pour une proprit. Les getters et setters sont gnrs ici seulement parce que
vous en aurez besoin pour interagir avec vos objets PHP.

Crer les Tables et le Schema


Vous avez maintenant une classe Product utilisable avec des informations de mapping permettant
Doctrine de savoir exactement comment le faire persister. Bien sr, vous n'avez toujours pas la
table product correspondante dans votre base de donnes. Heureusement, Doctrine peut crer
automatiquement toutes les tables de la base de donnes ncessaires aux entits connues dans votre
application. Pour ce faire, excutez la commande :
Listing 8-14

1 $ php app/console doctrine:schema:update --force

En fait, cette commande est incroyablement puissante. Elle compare ce quoi votre base de
donnes devrait ressembler (en se basant sur le mapping de vos entits) ce quoi elle ressemble
vraiment, et gnre le code SQL ncssaire pour mettre jour la base de donnes vers ce qu'elle doit
tre. En d'autres termes, si vous ajoutez une nouvelle proprit avec des mtadonnes mappes
sur Product et relancez cette tche, elle vous gnrera une requte alter table ncessaire pour
ajouter cette nouvelle colonne la table products existante.
Une meilleure faon de profiter de cette fonctionnalit est d'utiliser les migrations, qui vous
permettent de gnrer ces requtes SQL et de les stocker dans des classes de migration qui peuvent
tre lances systmatiquement sur vos serveurs de production dans le but de traquer et de migrer
vos schmas de base de donnes de manire sure et fiable.

Votre base de donnes a maintenant une table product totalement fonctionnelle avec des colonnes qui
correspondent aux mtadonnes que vous avez spcifies.

Persister des objets dans la base de donnes


Maintenant que vous avez mapp l'entit Product avec la table product correspondante, vous tes prt
faire persister des donnes dans la base de donnes. Depuis un contrleur, c'est trs facile. Ajoutez la
mthode suivante au DefaultController du bundle :
Listing 8-15

1
2
3
4
5
6
7
8
9
10
11

// src/Acme/StoreBundle/Controller/DefaultController.php
// ...
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
public function createAction()
{
$product = new Product();
$product->setName('A Foo Bar');
$product->setPrice('19.99');

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 99

12
13
14
15
16
17
18
19 }

$product->setDescription('Lorem ipsum dolor');


$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return new Response('Id du produit cr : '.$product->getId());

Si vous suivez les exemples au fur et mesure, vous aurez besoin de crer une route qui pointe vers
cette action pour voir si elle fonctionne.

Dcortiquons cet exemple :


lignes 9 12 Dans cette section, vous instanciez et travaillez avec l'objet product comme
n'importe quel autre objet PHP normal.
ligne 14 Cette ligne rcupre un objet gestionnaire d'entits (entity manager) de Doctrine, qui
est responsable de la gestion du processus de persistance et de rcupration des objets vers et
depuis la base de donnes.
ligne 15 La mthode persist() dit Doctrine de grer l'objet product. Cela ne cre pas
vraiment de requte dans la base de donnes (du moins pas encore).
ligne 16 Quand la mthode flush() est appele, Doctrine regarde dans tous les objets qu'il
gre pour savoir si ils ont besoin d'tre persists dans la base de donnes. Dans cet exemple,
l'objet $product n'a pas encore t persist, le gestionnaire d'entits excute donc une requte
INSERT et une ligne est cre dans la table product.
En fait, comme Doctrine a connaissance de toutes vos entits gres, lorsque vous appelez la
mthode flush(), il calcule un ensemble de changements global et excute la ou les requtes les
plus efficaces possible. Par exemple, si vous persistez un total de 100 objets Product et que vous
appelez ensuite la mthode flush(), Doctrine crera une unique requte prpare et la rutilisera
pour chaque insertion. Ce concept est nomm Unit de travail, et est utilis pour sa rapidit et son
efficacit.

Pour la cration et la suppression d'objet, le fonctionnement est le mme. Dans la prochaine section,
vous dcouvrirez que Doctrine est assez rus pour gnrer une requte UPDATE si l'enregistrement est dj
prsent dans la base de donnes.
Doctrine fournit une bibliothque qui vous permet de charger de manire automatise des donnes
de test dans votre projet (des fixtures ). Pour plus d'informations, lisez DoctrineFixturesBundle.

Rcuprer des objets dans la base de donnes


Rcuprer un objet depuis la base de donnes est encore plus facile. Par exemple, supposons que vous
avez configur une route pour afficher un Product spcifique en se basant sur la valeur de son id:
Listing 8-16

1 public function showAction($id)


2 {
3
$product = $this->getDoctrine()
4
->getRepository('AcmeStoreBundle:Product')

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 100

5
6
7
8
9
10
11
12
13
14 }

->find($id);
if (!$product) {
throw $this->createNotFoundException(
'Aucun produit trouv pour cet id : '.$id
);
}

// ... faire quelque chose comme envoyer l'objet $product un template

Vous pouvez raliser la mme chose sans crire de code en utilisant le raccourci @ParamConverter.
Pour plus de dtails, lisez la documentation du FrameworkExtraBundle.

Lorsque vous requtez pour un type particulier d'objet, vous utiliserez toujours ce qui est connu sous le
nom de dpt (ou repository ). Dites-vous qu'un dpt est une classe PHP dont le seul travail est
de vous aider rcuprer des entits d'une certaine classe. Vous pouvez accder au dpt d'une classe
d'entits avec:
Listing 8-17

1 $repository = $this->getDoctrine()
2
->getRepository('AcmeStoreBundle:Product');

La chane AcmeStoreBundle:Product est un raccourci que vous pouvez utiliser n'importe o


dans Doctrine au lieu du nom complet de la classe de l'entit (c..d
Acme\StoreBundle\Entity\Product). Tant que vos entits sont disponibles sous l'espace de nom
Entity de votre bundle, cela fonctionnera.

Une fois que vous disposez de votre dpt, vous pouvez accder toute sorte de mthodes utiles:
Listing 8-18

1
2
3
4
5
6
7
8
9
10
11
12

// requte par cl primaire (souvent "id")


$product = $repository->find($id);
// Noms de mthodes dynamiques en se basant sur un nom de colonne
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');
// trouver *tous* les produits
$products = $repository->findAll();
// trouver un groupe de produits en se basant sur une valeur de colonne
$products = $repository->findByPrice(19.99);

Bien sr, vous pouvez aussi gnrer des requtes complexes, ce que vous apprendrez dans la
section Requter des objets.

Vous pouvez aussi profiter des mthodes utiles findBy et findOneBy pour rcuprer facilement des objets
en vous basant sur des conditions multiples:
Listing 8-19

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 101

1
2
3
4
5
6
7
8

// requte un seul produit correspondant un nom et un prix


$product = $repository->findOneBy(array('name' => 'foo', 'price' => 19.99));
// requte tout les produits correspondant un nom, classs par prix
$products = $repository->findBy(
array('name' => 'foo'),
array('price' => 'ASC')
);

Lorsque vous affichez une page, vous pouvez voir combien de requtes sont faites dans le coin en
bas droite de votre barre d'outils de dbuggage.

Si vous cliquez sur l'icne, le profileur s'ouvrira, vous montrant les requtes exactes qui ont t
faites.

Mettre un objet jour


Une fois que vous avez rcupr un objet depuis Doctrine, le mettre jour est facile. Supposons que vous
avez une route qui mappe l'id d'un produit vers une action de mise jour dans un contrleur:
Listing 8-20

1 public function updateAction($id)


2 {
3
$em = $this->getDoctrine()->getManager();
4
$product = $em->getRepository('AcmeStoreBundle:Product')->find($id);
5
6
if (!$product) {
7
throw $this->createNotFoundException(
8
'Aucun produit trouv pour cet id : '.$id
9
);
10
}
11
12
$product->setName('Nom du nouveau produit!');
13
$em->flush();
14
15
return $this->redirect($this->generateUrl('homepage'));
16 }

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 102

Mettre jour l'objet ne ncessite que trois tapes :


1. Rcuprer l'objet depuis Doctrine;
2. Modifier l'objet;
3. Apeller la mthode flush() du gestionnaire d'entits
Notez qu'appeler $em->persist($product) n'est pas ncessaire. Souvenez-vous que cette mthode dit
simplement Doctrine de grer, ou regarder l'objet $product. Dans ce cas, comme vous avez rcupr
l'objet $product depuis Doctrine, il est dj surveill.

Supprimer un objet
Supprimer un objet est trs similaire, mais requiert un appel la mthode remove() du gestionnaire
d'entits :
Listing 8-21

1 $em->remove($product);
2 $em->flush();

Comme vous vous en doutez, la mthode remove() signale Doctrine que vous voulez supprimer l'entit
de la base de donnes. La vraie requte DELETE, cependant, n'est rellement excute que lorsque la
mthode flush() est appele.

Requter des objets


Vous avez dj vu comment les objets dpts vous permettaient d'excuter des requtes basiques sans
aucun travail:
Listing 8-22

1 $repository->find($id);
2
3 $repository->findOneByName('Foo');

Bien sr, Doctrine vous permet galement d'crire des requtes plus complexes en utilisant le Doctrine
Query Language (DQL). Le DQL est trs ressemblant au SQL except que vous devez imaginer que vous
requtez un ou plusieurs objets d'une classe d'entit (ex: Product) au lieu de requter des lignes dans une
table (ex: product).
Lorsque vous effectuez une requte l'aide de Doctrine, deux options s'offrent vous : crire une requte
Doctrine pure ou utilisez le constructeur de requte.

Requter des objets avec DQL


Imaginons que vous souhaitez rcuprer tous les produits dont le prix est suprieur 19.99, tris du
moins cher au plus cher. Depuis un contrleur, vous pouvez faire :
Listing 8-23

1
2
3
4
5
6
7
8
9

$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AcmeStoreBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult();

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 103

Si vous tes l'aise avec SQL, DQL ne devrait pas vous poser de problme. La plus grosse diffrence est
que vous devez penser en terme d' objets au lieu de lignes dans une base de donnes. Pour cette raison,
vous effectuez une slection depuis AcmeStoreBundle:Product et lui donnez p pour alias.
La mthode getResult() retourne un tableau de rsultats. Si vous ne souhaitez obtenir qu'un seul objet,
vous pouvez utiliser la mthode getSingleResult() la place:
Listing 8-24

1 $product = $query->getSingleResult();

La mthode getSingleResult() lve une exception Doctrine\ORM\NoResultException si aucun


rsultat n'est retourn et une exception Doctrine\ORM\NonUniqueResultException si plus d'un
rsultat est retourn. Si vous utilisez cette mthode, vous devrez sans doute l'entourer d'un bloc
try/catch pour vous assurer que seul un rsultat est retourn (si vous requtez quelque chose qui
pourrait retourner plus d'un rsultat):
Listing 8-25

1
2
3
4
5
6
7
8
9

$query = $em->createQuery('SELECT ...')


->setMaxResults(1);
try {
$product = $query->getSingleResult();
} catch (\Doctrine\Orm\NoResultException $e) {
$product = null;
}
// ...

La syntaxe du DQL est incroyablement puissante, vous permettant d'effectuer simplement des jointures
entre vos entits (le sujet des relations sera abord plus tard), regrouper, etc. Pour plus d'informations,
reportez-vous la documentation officielle de Doctrine : Doctrine Query Language7.

Dfinir des paramtres


Notez la prsence de la mthode setParameter(). En travaillant avec Doctrine, la bonne pratique
est de dfinir toutes les valeurs externes en tant que emplacements , ce qui a t fait dans la
requte ci-dessus :
Listing 8-26

1 ... WHERE p.price > :price ...

Vous pouvez alors dfinir la valeur de l'emplacement price en appelant la mthode


setParameter():
Listing 8-27

1 ->setParameter('price', '19.99')

Utiliser des paramtres au lieu de placer les valeurs directement dans la chane constituant la
requte permet de se prmunir des attaques de type injections de SQL et devrait toujours tre fait.
Si vous utilisez plusieurs paramtres, vous pouvez alors dfinir leurs valeurs d'un seul coup en
utilisant la mthode setParameters():
Listing 8-28

1 ->setParameters(array(
2
'price' => '19.99',
3
'name' => 'Foo',
4 ))

7. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 104

Utiliser le constructeur de requtes de Doctrine


Au lieu d'crire des requtes directement, vous pouvez alternativement utiliser le QueryBuilder
(constructeur de requtes) de Doctrine pour faire le mme travail en utilisant une jolie interface orienteobjet. Si vous utilisez un IDE, vous pourrez aussi profiter de l'auto-compltion en tapant le nom des
mthodes. De l'intrieur d'un contrleur:
Listing 8-29

1 $repository = $this->getDoctrine()
2
->getRepository('AcmeStoreBundle:Product');
3
4 $query = $repository->createQueryBuilder('p')
5
->where('p.price > :price')
6
->setParameter('price', '19.99')
7
->orderBy('p.price', 'ASC')
8
->getQuery();
9
10 $products = $query->getResult();

L'objet QueryBuilder contient toutes les mthodes ncessaires pour construire votre requte. En
appelant la mthode getQuery(), le constructeur de requtes retourne un objet standard Query, qui est
identique celui que vous avez construit dans la section prcdente.
Pour plus d'informations sur le constructeur de requtes de Doctrine, consultez la documentation de
Doctrine: Query Builder8

Classes de dpt personnalises


Dans les sections prcdentes, vous avez commenc construire et utiliser des requtes plus complexes
l'intrieur de vos contrleurs. Dans le but d'isoler, de tester et de rutiliser ces requtes, il est conseill de
crer des dpts personnaliss pour vos entits et d'y ajouter les mthodes contenant vos requtes.
Pour ce faire, ajouter le nom de la classe dpt vos informations de mapping.
Listing 8-30

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity(repositoryClass="Acme\StoreBundle\Entity\ProductRepository")
*/
class Product
{
//...
}

Doctrine peut gnrer la classe de dpt pour vous en lanant la mme commande que celle utilise
prcdemment pour gnrer les getters et setters :
Listing 8-31

1 $ php app/console doctrine:generate:entities Acme

Ensuite, ajoutez une mthode findAllOrderedByName() la classe frachement gnre. Cette mthode
requtera les entits Product, en les classant par ordre alphabtique.
Listing 8-32

8. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 105

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// src/Acme/StoreBundle/Entity/ProductRepository.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
}

Vous pouvez accder au gestionnaire d'entits par $this->getEntityManager() l'intrieur du


dpt.

Vous pouvez alors utiliser cette nouvelle mthode comme les mthodes par dfaut du dpt:
Listing 8-33

1 $em = $this->getDoctrine()->getManager();
2 $products = $em->getRepository('AcmeStoreBundle:Product')
3
->findAllOrderedByName();

En utilisant un dpt personnalis, vous avez toujours accs aux mthodes par dfaut telles que
find() et findAll().

Relations et associations entre les entits


Supposons que les produits de votre application appartiennent tous exactement une catgorie . Dans
ce cas, vous aurez besoin d'un objet Category et d'une manire de rattacher un objet Product un
objet Category. Commencez par crer l'entit Category. Puisque vous savez que vous aurez besoin que
Doctrine persiste votre classe, vous pouvez le laisser gnrer la classe pour vous.
Listing 8-34

1 $ php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Category" \


2
--fields="name:string(255)"

Cette commande gnre l'entit Category pour vous, avec un champ id, un champ name et les mthodes
getter et setter associes.

Mtadonnes de mapping de relations


Pour relier les entits Category et Product, commencez par crer une proprit products dans la classe
Category :
Listing 8-35

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 106

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// src/Acme/StoreBundle/Entity/Category.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class Category
{
// ...

/**
* @ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
}

Tout d'abord, comme un objet Category sera reli plusieurs objets Product, une proprit products
(un tableau) est ajoute pour stocker ces objets Product. Encore une fois, nous ne faisons pas cela parce
que Doctrine en a besoin, mais plutt parce qu'il est cohrent dans l'application que chaque Category
contienne un tableau d'objets Product.
Le code de la mthode __construct() est important, car Doctrine requiert que la proprit
$products soit un objet de type ArrayCollection. Cet objet ressemble et se comporte exactement
comme un tableau, mais avec quelque flexibilits supplmentaires. Si a vous drange, ne vous
inquitez pas. Imaginez juste que c'est un array et vous vous porterez bien.

La valeur targetEntity utilise plus haut peut faire rfrence n'importe quelle entit avec un espace
de nom valide, et pas seulement les entits dfinies dans la mme classe. Pour lier une entite
dfinie dans une autre classe ou un autre bundle, entrez l'espace de nom complet dans targetEntity.

Ensuite, comme chaque classe Product est relie exactement un objet Category, il serait bon d'ajouter
une proprit $category la classe Product :
Listing 8-36

1
2
3
4
5
6
7
8
9
10
11
12
13

// src/Acme/StoreBundle/Entity/Product.php
// ...
class Product
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
}

Finalement, maintenant que vous avez ajout une nouvelle proprit aux classes Category et Product,
dites Doctrine de rgnrer les getters et setters manquants pour vous :
Listing 8-37

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 107

1 $ php app/console doctrine:generate:entities Acme

Laissez de ct les mtadonnes de Doctrine pour un moment. Vous avez maintenant deux classes :
Category et Product avec une relation naturelle one-to-many. La classe Category peut contenir un
tableau de Product et l'objet Product peut contenir un objet Category. En d'autres termes, vous avez
construit vos classes de manire ce qu'elles aient un sens pour rpondre vos besoins. Le fait que les
donnes aient besoin d'tre persistes dans une base de donnes est toujours secondaire.
Maintenant, regardez les mtadonnes situes au-dessus de la proprit $category dans la classe
Product. Ces informations indiquent Doctrine que la classe associe est Category et que Doctrine
devrait stocker l'id de la catgorie dans un champ category_id prsent dans la table product. En
d'autres termes, l'objet Category associ sera stock dans la proprit $category, mais, de faon
transparente, Doctrine persistera la relation en stockant la valeur de l'id de la catgorie dans la colonne
category_id de la table product.

Les mtadonnes de la proprit $products de l'objet Category sont moins importantes, et indiquent
simplement Doctrine de regarder la proprit Product.category pour comprendre comment
l'association est mappe.

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 108

Avant que vous ne continuiez, assurez-vous que Doctrine ajoute la nouvelle table category, et la colonne
product.category_id, ainsi que la nouvelle cl trangre :
Listing 8-38

1 $ php app/console doctrine:schema:update --force

Cette commande ne devrait tre excute que lors du dveloppement. Pour une faon plus robuste
de mettre jour les bases de donnes de production, lisez l'article suivant: Doctrine migrations.

Sauver les entits associes


Maintenant, pour voir le code en action, imaginez que vous tes dans un contrleur :
Listing 8-39

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

// ...
use Acme\StoreBundle\Entity\Category;
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends Controller
{
public function createProductAction()
{
$category = new Category();
$category->setName('Main Products');
$product = new Product();
$product->setName('Foo');
$product->setPrice(19.99);
// lie ce produit une catgorie
$product->setCategory($category);
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->persist($product);
$em->flush();
return new Response(
'Id du produit cr : '.$product->getId().' et id de la catgorie :
'.$category->getId()
);
}
}

Maintenant, une simple ligne est ajoute aux tables category et product. La colonne
product.category_id du nouveau produit est dfinie comme la valeur de l'id de la nouvelle catgorie.
Doctrine gre la persistence de cette relation pour vous.

Rcuprer des objets associs


Lorsque vous rcuprez des objets associs, le processus que vous employez ressemble exactement celui
employ auparavant. Tout d'abord, rcuprez un objet $product pour accder sa Category associe:
Listing 8-40

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 109

1 public function showAction($id)


2 {
3
$product = $this->getDoctrine()
4
->getRepository('AcmeStoreBundle:Product')
5
->find($id);
6
7
$categoryName = $product->getCategory()->getName();
8
9
// ...
10 }

Dans cet exemple, vous requtez tout d'abord un objet Product en vous basant sur l'id du produit. Cela
genre une requte uniquement pour les donnes du produit et hydrate l'objet $product avec ces donnes.
Plus tard, lorsque vous appelez $product->getCategory()->getName(), Doctrine effectue une seconde
requte de faon transparente pour trouver la Category qui est associ ce Product. Il prpare l'objet
$category et vous le renvoie.

Le plus important est que vous accdiez la catgorie associe au produit, mais que les donnes de
cette catgorie ne sont rellement rcupres que lorsque vous demandez la catgorie (on parle alors de
chargement fainant ou lazy loading ).
Vous pouvez aussi faire cette requte dans l'autre sens:
Listing 8-41

1 public function showProductAction($id)


2 {
3
$category = $this->getDoctrine()
4
->getRepository('AcmeStoreBundle:Category')
5
->find($id);
6
7
$products = $category->getProducts();
8
9
// ...
10 }

Dans ce cas, la mme chose se produit : vous requtez tout d'abord un simple objet Category, et Doctrine
effectue alors une seconde requte pour rcuprer les objets Product associs, mais uniquement une fois

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 110

que/si vous les demandez (c--d si vous appelez ->getProducts()). La variable $products est un tableau
de tous les objets Product associs l'objet Category donns via leurs valeurs category_id.

Associations et classes mandataires


Ce mcanisme de chargement fainant est possible car, quand c'est ncessaire, Doctrine
retourne un objet mandataire (proxy) au lieu des vrais objets. Regardez de plus prs l'exemple
ci-dessous:
Listing 8-42

1
2
3
4
5
6
7
8

$product = $this->getDoctrine()
->getRepository('AcmeStoreBundle:Product')
->find($id);
$category = $product->getCategory();

// affiche "Proxies\AcmeStoreBundleEntityCategoryProxy"
echo get_class($category);

Cet objet mandataire tend le vrai objet Category, et l'air de se comporter exactement de la
mme manire. La diffrence est que, en utilisant un objet mandataire, Doctrine peut retarder le
requtage des vraies donnes de la Category jusqu'a ce que vous en ayez rellement besoin (en
appelant par exemple $category->getName()).
Les classes mandataires sont gnres par Doctrine et stockes dans le rpertoire du cache. Mme
si vous ne remarquerez probablement jamais que votre objet $category est en fait un objet
mandataire, il est important de le garder l'esprit.
Dans la prochaine section, lorsque vous rcuprerez les donnes du produit et de la catgorie d'un
seul coup (via un join), Doctrine retournera un vrai objet Category, car rien ne sera charg de
manire fainante.

Faire des jointures avec des enregistrements associs


Dans les exemples ci-dessus, deux requtes ont t faites - une pour l'objet original (par exemple, une
Category), et une pour le(s) objet(s) associ(s) (par exemple, les objets Product)
N'oubliez pas que vous pouvez voir toutes les requtes effectues en utilisant la barre d'outils de
dbuggage.

Bien sr, si vous savez ds le dbut que vous aurez besoin d'accder aux deux objets, vous pouvez viter
de produire une deuxime requte en ajoutant une jointure dans la requte originale. Ajouter le code
suivant la classe ProductRepository:
Listing 8-43

1 // src/Acme/StoreBundle/Entity/ProductRepository.php
2 public function findOneByIdJoinedToCategory($id)
3 {
4
$query = $this->getEntityManager()
5
->createQuery('
6
SELECT p, c FROM AcmeStoreBundle:Product p
7
JOIN p.category c
8
WHERE p.id = :id'
9
)->setParameter('id', $id);
10
11
try {
12
return $query->getSingleResult();

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 111

13
14
15
16 }

} catch (\Doctrine\ORM\NoResultException $e) {


return null;
}

Maintenant, vous pouvez utiliser cette mthode dans votre contrleur pour requter un objet Product et
sa Category associe avec une seule requte:
Listing 8-44

1 public function showAction($id)


2 {
3
$product = $this->getDoctrine()
4
->getRepository('AcmeStoreBundle:Product')
5
->findOneByIdJoinedToCategory($id);
6
7
$category = $product->getCategory();
8
9
// ...
10 }

Plus d'informations sur les associations


Cette section a introduit le type le plus commun d'associations entre les entits, la relation one-tomany. Pour plus de dtails et d'exemples avancs sur comment utiliser les autre types de relations
(comme one-to-one, ou many-to-many), consultez la documentation de Doctrine: Association Mapping
Documentation9.
Si vous utilisez les annotations, vous devrez prfixer les annotations avec ORM\ (par exemple:
ORM\OneToMany), ce qui n'est pas spcifi dans la documentation de Doctrine. Vous aurez aussi
besoin d'inclure la ligne use Doctrine\ORM\Mapping as ORM; pour importer le prfixe
d'annotation ORM.

Configuration
Doctrine est entirement configurable, mme si vous n'aurez sans doute jamais besoin de vous embter
avec la plupart de ses options. Pour obtenir des informations sur la configuration de Doctrine, rendezvous dans la section correspondante du manuel de rfrence.

Callbacks et cycle de vie


Parfois, vous voudrez effectuer des actions juste avant ou aprs qu'une entit ait t insre, mise jour
ou supprime. Ces actions sont connues sous le nom de callbacks du cycle de vie (lifecycle), car il
s'agit de callbacks (mthodes) qui peuvent tre appels divers moments du cycle de vie de votre entit
(par exemple lorsque l'entit est insre, mise jour, supprime, etc.).
Si vous utilisez des annotations pour vos mtadonnes, commencez par activer les callbacks du cycle de
vie. Si vous utilisez YAML ou XML pour votre mapping, ce n'est pas ncessaire :
Listing 8-45

9. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 112

/**
* @ORM\Entity()
* @ORM\HasLifecycleCallbacks()
*/
class Product
{
// ...
}

1
2
3
4
5
6
7
8

Dsormais, vous pouvez dire Doctrine d'xecuter une mthode n'importe quel vnement du cycle de
vie. Par exemple, supposons que vous souhaitez dfinir une date created la date courante, uniquement
lorsque l'entit est persiste pour la premire fois (c--d insre) :
Listing 8-46

/**
* @ORM\PrePersist
*/
public function setCreatedValue()
{
$this->created = new \DateTime();
}

1
2
3
4
5
6
7

L'exemple ci-dessus suppose que vous avez cr et mapp une proprit created (qui n'est pas
montre ici).

Maintenant, juste avant que l'entit soit initialement persiste, Doctrine appelera automatiquement la
mthode et le champ created sera dfini la date courante.
Vous pouvez procder ainsi pour n'importe quel autre vnement du cycle de vie, ce qui inclut :

preRemove
postRemove
prePersist
postPersist
preUpdate
postUpdate
postLoad
loadClassMetadata

Pour plus d'informations sur la signification de ces vnements du cycle de vie et sur leurs callbacks en
gnral, rfrez-vous la documentation de Doctrine: Lifecycle Events documentation10.

10. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 113

Callbacks du cycle de vie et couteurs d'vnements


Notez que la mthode setCreatedValue() ne prend pas d'argument. C'est toujours le cas des
callbacks du cycle de vie, et c'est intentionnel : ces callbacks doivent tre de simples mthodes et
contiennent des transformations de donnes internes l'entit (ex: dfinir un champ cr ou mis
jour, gnrer une valeur de slug...).
Si vous souhaitez faire des montages plus lourds - comme une identification ou envoyer un mail
- vous devez crire une classe externe et l'enregistrer pour couter ou s'abonner aux vnements,
puis lui donner les accs toutes les ressources dont vous aurez besoin. Pour plus d'informations,
lisez Comment enregistrer des listeners ( couteurs en franais) et des souscripteurs d'vnement.

Les extensions de Doctrine: Timestampable, Sluggable, etc.


Doctrine est trs flexible, et il existe un certain nombre d'extensions tierces qui permettent de faciliter
les tches courantes sur vos entits. Elles incluent diverses outils comme Sluggable, Timestampable,
Loggable, Translatable, et Tree.
Pour plus d'informations sur comment trouver et utiliser ces extensions, regardez l'article du cookbook
relatif l'utilisation des extensions Doctrine.

Rfrence des types de champs de Doctrine


Doctrine contient un grand nombre de types de champs. Chacun mappe un type de donnes PHP vers
un type de colonne spcifique la base de donnes que vous utilisez. Les types suivants sont supports
par Doctrine :
Chanes de caractres
string (utilis pour des chanes courtes)
text (utilis pour des chanes longues)
Nombres

integer
smallint
bigint
decimal
float

Dates et heures (ces champs utilisent un objet PHP DateTime11)


date
time
datetime
Autre types
boolean
object (srialis et stock dans un champ CLOB)
array (srialis et stock dans un champ CLOB)
Pour plus d'informations, lisez la documentation Doctrine Types de mapping Doctrine12.
11. http://php.net/manual/en/class.datetime.php

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 114

Options des champs


Un ensemble d'options peut tre appliqu chaque champ. Les options disponibles incluent type (valant
string par dfaut), name, length, unique et nullable. Regardons quelques exemples :
Listing 8-47

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

/**
* Une chane de caractres de longueur 255 qui ne peut pas tre nulle
* (refltant les valeurs par dfaut des options "type", "length" et *nullable);
*
* @ORM\Column()
*/
protected $name;
/**
* Une chane de longueur 150 qui sera persiste vers une colonne "email_address"
* et a un index unique.
*
* @ORM\Column(name="email_address", unique=true, length=150)
*/
protected $email;

Il existe d'autre options qui ne sont pas listes ici. Pour plus de dtails, lisez Property Mapping
documentation13.

Commandes en console
L'intgration de l'ORM Doctrine2 offre plusieurs commandes de console sous l'espace de nom doctrine.
Pour voir la liste de ces commandes, vous pouvez excutez la console sans aucun argument :
Listing 8-48

1 $ php app/console

Une liste des commandes disponibles s'affichera, la plupart d'entre elles commencent par le prfixe
doctrine:. Vous pouvez obtenir plus d'informations sur n'importe laquelle de ces commandes (ou
n'importe quelle commande Symfony) en lanant la commande help. Par exemple, pour obtenir des
informations sur la commande doctrine:database:create, excutez :
Listing 8-49

1 $ php app/console help doctrine:database:create

Voici une liste non exhaustive de commandes intressantes :


doctrine:ensure-production-settings - teste si l'environnement actuel est configur de
manire optimale pour la production. Elle devrait toujours tre excute dans un
environnement prod :
Listing 8-50

1 $ php app/console doctrine:ensure-production-settings --env=prod

doctrine:mapping:import - permet Doctrine d'introspecter une base de donnes existante


pour crer les informations de mapping. Pour plus d'informations, voir Comment gnrer des
Entits partir d'une base de donnes existante.
12. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#doctrine-mapping-types
13. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#property-mapping

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 115

doctrine:mapping:info - vous donne toutes les entits dont Doctrine a connaissance et s'il
existe des erreurs basiques dans leur mapping.
doctrine:query:dql et doctrine:query:sql - vous permet d'effectuer des commandes DQL
ou SQL directement en ligne de commande.
Pour pouvoir charger des donnes d'installation (fixtures), vous devrez installer le bundle
DoctrineFixtureBundle. Pour apprendre comment le faire, lisez le chapitre du Cookbook :
"DoctrineFixturesBundle"

Cette page montre comment Doctrine fonctionne au sein d'un contrleur. Mais vous voulez peut
tre travailler avec Doctrine ailleurs dans votre application. La mthode getDoctrine()14 de la
classe Controller retourne le service doctrine. Vous pouvez travailler avec de la mme manire,
en l'injectant dans vos propres services. Lisez Service Container pour savoir comment crer vos
propres services.

Rsum
Avec Doctrine, vous pouvez tout d'abord vous focaliser sur vos objets et sur leur utilit dans votre
application, puis vous occuper de leur persistance ensuite. Vous pouvez faire cela car Doctrine vous
permet d'utiliser n'importe quel objet PHP pour stocker vos donnes et se fie aux mtadonnes de
mapping pour faire correspondre les donnes d'un objet une table particulire de la base de donnes.
Et mme si Doctrine tourne autour d'un simple concept, il est incroyablement puissant, vous permettant
de crer des requtes complexes et de vous abonner des vnements qui vous permettent d'effectuer
diffrentes actions au cours du cycle de vie de vos objets.
Pour plus d'informations sur Doctrine, lisez la section Doctrine du Cookbook: cookbook, qui inclut les
articles suivants :
DoctrineFixturesBundle
Comment utiliser les extensions Doctrine: Timestampable, Sluggable, Translatable, etc.

14. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#getDoctrine()

PDF brought to you by


generated on January 14, 2015

Chapter 8: Doctrine et les bases de donnes | 116

Chapter 9

Propel et les bases de donnes


Regardons la vrit en face, l'une des tches les plus communes et difficiles pour quelconque application
implique de devoir persister et lire de l'information dans et depuis une base de donnes. Symfony2 ne
vient pas avec un ORM intgr, mais l'intgration de Propel reste nanmoins aise. Pour commencer,
lisez Travailler Avec Symfony21.

Un Exemple Simple : Un Produit


Dans cette section, vous allez configurer votre base de donnes, crer un objet Product, le persister dans
la base de donnes et ensuite le rcuprer.

Codez en mme temps que vous lisez cet exemple


Si vous voulez codez en mme temps que vous parcourez l'exemple de ce chapitre, crez un
AcmeStoreBundle via :
Listing 9-1

1 $ php app/console generate:bundle --namespace=Acme/StoreBundle

Configurer la Base de Donnes


Avant de pouvoir dmarrer, vous allez avoir besoin de configurer les informations de connexion de votre
base de donnes. Par convention, ces informations sont gnralement configures dans un fichier app/
config/parameters.yml :
Listing 9-2

1 # app/config/parameters.yml
2 parameters:
3
database_driver:
mysql
4
database_host:
localhost
5
database_name:
test_project

1. http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 117

6
7
8

database_user:
root
database_password: password
database_charset: UTF8

Dfinir la configuration via parameters.yml n'est qu'une convention. Les paramtres dfinis dans
ce fichier sont rfrencs par le fichier de la configuration principale lorsque vous dfinissez Propel
:
Listing 9-3

1 propel:
2
dbal:
3
driver:
"%database_driver%"
4
user:
"%database_user%"
5
password:
"%database_password%"
6
dsn:
"%database_driver%:host=%database_host%;dbname=%database_name%;charset=%database_charset%"

Maintenant que Propel connat votre base de donnes, Symfony2 peut crer cette dernire pour vous :
Listing 9-4

1 $ php app/console propel:database:create

Dans cet exemple, vous avez une connexion configure, nomme default. Si vous voulez
configurer plus d'une connexion, lisez la partie sur la configuration <Travailler avec Symfony2 Configuration>`_.

Crer une Classe Modle


Dans le monde de Propel, les classes ActiveRecord sont connues en tant que modles car les classes
gnres par Propel contiennent un peu de logique mtier.
Pour les gens qui utilisent Symfony2 avec Doctrine2, les modles sont quivalents aux entits.

Supposons que vous construisiez une application dans laquelle des produits doivent tre affichs. Tout
d'abord, crez un fichier schema.xml dans le rpertoire Resources/config de votre AcmeStoreBundle :
Listing 9-5

1 <?xml version="1.0" encoding="UTF-8"?>


2 <database name="default" namespace="Acme\StoreBundle\Model" defaultIdMethod="native">
3
<table name="product">
4
<column name="id" type="integer" required="true" primaryKey="true"
5 autoIncrement="true" />
6
<column name="name" type="varchar" primaryString="true" size="100" />
7
<column name="price" type="decimal" />
8
<column name="description" type="longvarchar" />
9
</table>
</database>

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 118

Construire le Modle
Aprs avoir cr votre schema.xml, gnrez votre modle partir de ce dernier en excutant :
Listing 9-6

1 $ php app/console propel:model:build

Cela va gnrer chaque classe modle afin que vous puissiez dvelopper rapidement votre application
dans le rpertoire Model/ de votre bundle AcmeStoreBundle.

Crer les Tables et le Schma de la Base de Donnes


Maintenant, vous avez une classe Product utilisable et tout ce dont vous avez besoin pour persister un
produit. Bien sr, pour le moment, vous ne disposez pas de la table product correspondante dans votre
base de donnes. Heureusement, Propel peut automatiquement crer toutes les tables de base de donnes
ncessaires chaque modle connu de votre application. Pour effectuer cela, excutez :
Listing 9-7

1 $ php app/console propel:sql:build


2 $ php app/console propel:sql:insert --force

Votre base de donnes possde dsormais une table product entirement fonctionnelle avec des colonnes
qui correspondent au schma que vous avez spcifi.
Vous pouvez excuter les trois dernires commandes de manire combine en utilisant la
commande suivante : php app/console propel:build --insert-sql.

Persister des Objets dans la Base de Donnes


Maintenant que vous avez un objet Product et une table product correspondante, vous tes prt
persister des donnes dans la base de donnes. Depuis un contrleur, cela est assez facile. Ajoutez la
mthode suivante au DefaultController du bundle cr plus haut:
Listing 9-8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// src/Acme/StoreBundle/Controller/DefaultController.php
use Acme\StoreBundle\Model\Product;
use Symfony\Component\HttpFoundation\Response;
// ...
public function createAction()
{
$product = new Product();
$product->setName('A Foo Bar');
$product->setPrice(19.99);
$product->setDescription('Lorem ipsum dolor');
$product->save();
return new Response('Created product id '.$product->getId());
}

Dans ce bout de code, vous instanciez et travaillez avec l'objet product. Lorsque vous appelez la mthode
save() sur ce dernier, vous persistez le produit dans la base de donnes. Pas besoin d'utiliser d'autres
services, l'objet sait comment se persister lui-mme.

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 119

Si vous codez tout en lisant cet exemple, vous allez avoir besoin de crer une route qui pointe vers
cette action pour la voir fonctionner.

Rcuprer des Objets depuis la Base de Donnes


Rcuprer un objet depuis la base de donnes est encore plus simple. Par exemple, supposons que vous
ayez configur une route pour afficher un Produit spcifique bas sur la valeur de son id:
Listing 9-9

1
2
3
4
5
6
7
8
9
10
11
12
13

use Acme\StoreBundle\Model\ProductQuery;
public function showAction($id)
{
$product = ProductQuery::create()
->findPk($id);
if (!$product) {
throw $this->createNotFoundException('No product found for id '.$id);
}

// faites quelque chose, comme passer l'objet $product un template


}

Mettre jour un Objet


Une fois que vous avez rcupr un objet depuis Propel, le mettre jour est facile. Supposons que vous
ayez une route qui fasse correspondre un id de produit une action de mise jour dans un contrleur:
Listing 9-10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

use Acme\StoreBundle\Model\ProductQuery;
public function updateAction($id)
{
$product = ProductQuery::create()
->findPk($id);
if (!$product) {
throw $this->createNotFoundException('No product found for id '.$id);
}
$product->setName('New product name!');
$product->save();
return $this->redirect($this->generateUrl('homepage'));
}

Mettre jour un objet implique seulement trois tapes :


1. rcuprer l'objet depuis Propel ;
2. modifier l'objet ;
3. le sauvegarder.

Supprimer un Objet
Supprimer un objet est trs similaire, except que cela requiert un appel la mthode delete() sur
l'objet:

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 120

Listing 9-11

1 $product->delete();

Effectuer des requtes pour rcuprer des Objets


Propel fournit des classes Query gnres pour excuter aussi bien des requtes basiques que des requtes
complexes, et cela sans aucun effort de votre part:
Listing 9-12

1 \Acme\StoreBundle\Model\ProductQuery::create()->findPk($id);
2
3 \Acme\StoreBundle\Model\ProductQuery::create()
4
->filterByName('Foo')
5
->findOne();

Imaginez que vous souhaitiez effectuer une requte sur des produits cotant plus de 19.99, ordonns du
moins cher au plus cher. Depuis l'un de vos contrleurs, faites ce qui suit:
Listing 9-13

1 $products = \Acme\StoreBundle\Model\ProductQuery::create()
2
->filterByPrice(array('min' => 19.99))
3
->orderByPrice()
4
->find();

Trs facilement, vous obtenez vos produits en construisant une requte de manire oriente objet. Aucun
besoin de perdre du temps crire du SQL ou quoi que ce soit d'autre, Symfony2 offre une manire de
programmer totalement oriente objet et Propel respecte la mme philosophie en fournissant une couche
d'abstraction ingnieuse.
Si vous voulez rutiliser certaines requtes, vous pouvez ajouter vos propres mthodes la classe
ProductQuery:
Listing 9-14

1
2
3
4
5
6
7
8
9
10

// src/Acme/StoreBundle/Model/ProductQuery.php
class ProductQuery extends BaseProductQuery
{
public function filterByExpensivePrice()
{
return $this
->filterByPrice(array('min' => 1000))
}
}

Mais notez que Propel gnre beaucoup de mthodes pour vous et qu'une simple mthode
findAllOrderedByName() peut tre crite sans aucun effort:
Listing 9-15

1 \Acme\StoreBundle\Model\ProductQuery::create()
2
->orderByName()
3
->find();

Relations/Associations
Supposez que les produits de votre application appartiennent tous une seule catgorie . Dans ce cas,
vous aurez besoin d'un objet Category et d'une manire de lier un objet Product un objet Category.
PDF brought to you by
generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 121

Commencez par ajouter la dfinition de category dans votre schema.xml :


Listing 9-16

1 <database name="default" namespace="Acme\StoreBundle\Model" defaultIdMethod="native">


2
<table name="product">
3
<column name="id" type="integer" required="true" primaryKey="true"
4 autoIncrement="true" />
5
<column name="name" type="varchar" primaryString="true" size="100" />
6
<column name="price" type="decimal" />
7
<column name="description" type="longvarchar" />
8
9
<column name="category_id" type="integer" />
10
<foreign-key foreignTable="category">
11
<reference local="category_id" foreign="id" />
12
</foreign-key>
13
</table>
14
15
<table name="category">
16
<column name="id" type="integer" required="true" primaryKey="true"
17 autoIncrement="true" />
18
<column name="name" type="varchar" primaryString="true" size="100" />
</table>
</database>

Crez les classes :


Listing 9-17

1 $ php app/console propel:model:build

Assumons que vous ayez des produits dans votre base de donnes, vous ne souhaitez pas les perdre.
Grce aux migrations, Propel va tre capable de mettre jour votre base de donnes sans perdre aucune
donne.
Listing 9-18

1 $ php app/console propel:migration:generate-diff


2 $ php app/console propel:migration:migrate

Votre base de donnes a t mise jour, vous pouvez continuer crire votre application.

Sauvegarder des Objets Lis


Maintenant, voyons le code en action. Imaginez que vous soyez dans un contrleur:
Listing 9-19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// ...
use Acme\StoreBundle\Model\Category;
use Acme\StoreBundle\Model\Product;
use Symfony\Component\HttpFoundation\Response;
// ...
class DefaultController extends Controller
{
public function createProductAction()
{
$category = new Category();
$category->setName('Main Products');
$product = new Product();
$product->setName('Foo');
$product->setPrice(19.99);
// lie ce produit la catgorie dfinie plus haut

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 122

18
$product->setCategory($category);
19
20
// sauvegarde l'ensemble
21
$product->save();
22
23
return new Response(
24
'Created product id: '.$product->getId().' and category id:
25 '.$category->getId()
26
);
27
}
}

Maintenant, une seule ligne est ajoute aux deux tables category et product. La colonne
product.category_id pour le nouveau produit est dfinie avec l'id de la nouvelle catgorie. Propel gre
la persistance de cette relation pour vous.

Rcuprer des Objets Lis


Lorsque vous avez besoin de rcuprer des objets lis, votre processus ressemble la rcupration d'un
attribut dans l'exemple prcdent. Tout d'abord, rcuprez un objet $product et ensuite accdez sa
Category lie:
Listing 9-20

1
2
3
4
5
6
7
8
9
10
11
12
13

// ...
use Acme\StoreBundle\Model\ProductQuery;
public function showAction($id)
{
$product = ProductQuery::create()
->joinWithCategory()
->findPk($id);
$categoryName = $product->getCategory()->getName();

// ...
}

Notez que dans l'exemple ci-dessus, seulement une requte a t effectue.

Plus d'informations sur les Associations


Vous trouverez plus d'informations sur les relations en lisant le chapitre ddi sur les Relations2.

Callbacks et Cycles de Vie


De temps en temps, vous avez besoin d'effectuer une action juste avant ou juste aprs qu'un objet soit
insr, mis jour, ou supprim. Ces types d'actions sont connus en tant que callbacks de cycles de vie
ou hooks , comme ce sont des mthodes callbacks que vous devez excuter diffrentes tapes du
cycle de vie d'un objet (par exemple : l'objet est insr, mis jour, supprim, etc).
Pour ajouter un hook , ajoutez simplement une nouvelle mthode la classe de l'objet:
Listing 9-21

2. http://www.propelorm.org/documentation/04-relationships.html

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 123

1
2
3
4
5
6
7
8
9
10
11

// src/Acme/StoreBundle/Model/Product.php
// ...
class Product extends BaseProduct
{
public function preInsert(\PropelPDO $con = null)
{
// faites quelque chose avant que l'objet soit insr
}
}

Propel fournit les hooks suivants :

preInsert() code excut avant l'insertion d'un nouvel objet


postInsert() code excut aprs l'insertion d'un nouvel objet
preUpdate() code excut avant la mise jour d'un objet existant
postUpdate() code excut aprs la mise jour d'un objet existant
preSave() code excut avant la sauvegarde d'un objet (nouveau ou existant)
postSave() code excut aprs la sauvegarde d'un objet (nouveau ou existant)
preDelete() code excut avant la suppression d'un objet
postDelete() code excut aprs la suppression d'un objet

Comportements
Tous les comportements fournis par Propel fonctionnent avec Symfony2. Pour avoir plus d'informations
sur la manire d'utiliser les comportements Propel, jetez un oeil la Section de Rfrence des
Comportements3.

Commandes
Vous devriez lire la section ddie aux commandes Propel dans Symfony24.

3. http://www.propelorm.org/documentation/#behaviors_reference
4. http://www.propelorm.org/cookbook/symfony2/working-with-symfony2#the_commands

PDF brought to you by


generated on January 14, 2015

Chapter 9: Propel et les bases de donnes | 124

Chapter 10

Les Tests
A chaque fois que vous crivez une nouvelle ligne de code, vous ajoutez aussi potentiellement de
nouveaux bugs. Pour crer de meilleures applications et plus fiables, vous devriez tester votre code avec
des tests fonctionnels et des tests unitaires.

Le Framework de Test PHPUnit


Symfony2 intgre une bibliothque indpendante - appele PHPUnit - qui vous fournit un framework de
test complet. Notez que ce chapitre ne couvre pas PHPUnit, qui dispose de sa propre documentation1.
Symfony2 fonctionne avec PHPUnit 3.5.11 au minimum, mais la version 3.6.4 est ncessaire pour
tester le coeur du framework.

Chaque test - que ce soit un test fonctionnel ou un test unitaire - est une classe PHP qui devrait se trouver
dans le sous-rpertoire Tests/ de vos bundles. Si vous suivez cette rgle, alors vous pouvez lancer les tests
de votre application avec la commande suivante :
Listing 10-1

1 # spcifier le rpertoire de configuration


2 $ phpunit -c app/

L'option -c indique PHPUnit de chercher le fichier de configuration dans le rpertoire app/. Si vous
voulez en savoir plus sur les options de PHPUnit, jetez un oeil au fichier app/phpunit.xml.dist.
La couverture de code peut tre gnre avec l'option --coverage-html.

1. http://www.phpunit.de/manual/3.5/en/

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 125

Tests unitaires
Un test unitaire teste habituellement une classe PHP spcifique. Si vous voulez tester le comportement
gnral de votre application, lisez la section sur les Tests Fonctionnels.
crire des tests unitaires avec Symfony2 n'est pas diffrent d'crire des tests unitaires standards PHPUnit.
Supposez, par exemple, que vous avez un classe incroyablement simple appele Calculator dans le
rpertoire Utility/ de votre bundle :
Listing 10-2

1
2
3
4
5
6
7
8
9
10

// src/Acme/DemoBundle/Utility/Calculator.php
namespace Acme\DemoBundle\Utility;
class Calculator
{
public function add($a, $b)
{
return $a + $b;
}
}

Pour tester cela, crez le fichier CalculatorTest dans le dossier Tests/Utility de votre bundle :
Listing 10-3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
namespace Acme\DemoBundle\Tests\Utility;
use Acme\DemoBundle\Utility\Calculator;
class CalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testAdd()
{
$calc = new Calculator();
$result = $calc->add(30, 12);

// vrifie que votre classe a correctement calcul!


$this->assertEquals(42, $result);
}
}

Par convention, le sous-rpertoire Tests/ doit reproduire la structure de votre bundle. Donc, si
vous testez une classe du rpertoire Utility/ du bundle, mettez le test dans le rpertoire Tests/
Utility/.

Comme dans votre vraie application, le chargement automatique est activ via le fichier
bootstrap.php.cache (comme configur par dfaut dans le fichier phpunit.xml.dist).
Excuter les tests pour un fichier ou rpertoire donn est aussi trs facile :
Listing 10-4

1
2
3
4
5
6
7
8

# lancer tous les tests du rpertoire Utility


$ phpunit -c app src/Acme/DemoBundle/Tests/Utility/
# lancer les tests de la classe Calculator
$ phpunit -c app src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
# lancer tous les tests d'un Bundle
$ phpunit -c app src/Acme/DemoBundle/

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 126

Tests Fonctionnels
Les tests fonctionnels vrifient l'intgration des diffrentes couches d'une application (du routing
jusqu'aux vues). Ils ne sont pas diffrents des tests unitaires tant que PHPUnit est concern, mais ils
possdent un workflow trs spcifique :

Faire une requte;


Tester la rponse;
Cliquer sur un lien ou soumettre un formulaire;
Tester la rponse;
Recommencer, ainsi de suite.

Votre premier test fonctionnel


Les tests fonctionnels sont de simples fichiers PHP qui se trouvent dans le rpertoire Tests/Controller
du bundle. Si vous voulez tester des pages gres par la classe DemoController, commencez par crer un
nouveau fichier DemoControllerTest.php qui tend la classe spciale WebTestCase.
Par exemple, la Symfony2 Standard Edition fournit un simple test fonctionnel pour son DemoController
(DemoControllerTest2) :
Listing 10-5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php
namespace Acme\DemoBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DemoControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/demo/hello/Fabien');
$this->assertGreaterThan(0, $crawler->filter('html:contains("Hello
Fabien")')->count());
}
}

Pour lancer vos tests fonctionnels, la classe WebTestCase initie (bootstrap) le noyau (kernel) de
votre application. Dans la plupart des cas, cela fonctionnera automatiquement. Cependant, si votre
kernel n'est pas dans le rpertoire habituel, vous devrez modifier le fichier phpunit.xml.dist pour
dfinir la variable d'environnement KERNEL_DIR afin qu'elle pointe vers le rpertoire du kernel :
Listing 10-6

1 <phpunit>
2
<!-- ... -->
3
<php>
4
<server name="KERNEL_DIR" value="/path/to/your/app/" />
5
</php>
6
<!-- ... -->
7 </phpunit>

2. https://github.com/symfony/symfony-standard/blob/master/src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 127

La mthode createClient() retourne un client, qui ressemble au navigateur que vous utilisez pour
surfer sur vos sites:
Listing 10-7

1 $crawler = $client->request('GET', '/demo/hello/Fabien');

La mthode request() (plus d'informations sur la mthode Request) retourne un objet Crawler3 qui
peut tre utilis pour slectionner les lments dans la Rponse, cliquer sur les liens, et soumettre des
formulaires.
Le Crawler peut tre utilis seulement si le contenu de la Rponse est un document XML ou
HTML. Pour obtenir le contenu brut, appelez la mthode $client->getResponse()>getContent().

Cliquez sur un lien en le slectionnant avec le Crawler en utilisant soit une expression XPath ou un
slecteur CSS, puis utilisez le Client pour cliquer dessus. Par exemple, le code suivant trouve tous les liens
avec le text Greet, puis slectionne le second et clique dessus :
Listing 10-8

1 $link = $crawler->filter('a:contains("Greet")')->eq(1)->link();
2
3 $crawler = $client->click($link);

Soumettre un formulaire est trs similaire : slectionnez un bouton de ce dernier, rcrivez quelques-unes
de ses valeurs si besoin est puis soumettez-le :
Listing 10-9

1
2
3
4
5
6
7
8

$form = $crawler->selectButton('submit')->form();

// dfinit certaines valeurs


$form['name'] = 'Lucas';
$form['form_name[subject]'] = 'Hey there!';
// soumet le formulaire
$crawler = $client->submit($form);

Le formulaire peut galement grer les envois et contient des mthodes pour remplir les diffrents
types des champs de formulaire (ex select() et tick()). Pour plus de dtails, lisez la section
Formulaires.

Maintenant que vous pouvez naviguer aisment travers une application, utilisez les assertions pour
tester qu'elle fait effectivement ce que vous souhaitez qu'elle fasse. Utilisez le Crawler pour faire des
assertions sur le DOM :
Listing 10-10

1 // affirme que la rponse correspond au slecteur CSS donn


2 $this->assertGreaterThan(0, $crawler->filter('h1')->count());

Ou alors, testez directement le contenu de la Rponse si vous voulez juste vrifier qu'il contient un certain
texte, ou si la Rponse n'est pas un document XML/HTML :
Listing 10-11

1 $this->assertRegExp('/Hello Fabien/', $client->getResponse()->getContent());

3. http://api.symfony.com/2.6/Symfony/Component/DomCrawler/Crawler.html

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 128

Plus d'infos sur la mthode request():


La signature complte de la mthdode request() est:
Listing 10-12

1 request(
2
$method,
3
$uri,
4
array $parameters = array(),
5
array $files = array(),
6
array $server = array(),
7
$content = null,
8
$changeHistory = true
9 )

Le tableau server contient les valeurs brutes que vous trouveriez normalement dans la variable
superglobale $_SERVER4. Par exemple, pour dfinir les enttes HTTP Content-Type et Referer, vous
procderiez comme suit (pensez au prfixe HTTP_ pour les en-ttes non standard) :

Listing 10-13

1 $client->request(
2
'GET',
3
'/demo/hello/Fabien',
4
array(),
5
array(),
6
array(
7
'CONTENT_TYPE' => 'application/json',
8
'HTTP_REFERER' => '/foo/bar',
9
'HTTP_X-Requested-With' => 'XMLHttpRequest',
10
)
11 );

4. http://php.net/manual/en/reserved.variables.server.php

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 129

Assertions Utiles
Afin que vous dmarriez plus rapidement, voici une liste des assertions les plus communes et utiles
:
Listing 10-14

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// Vrifie qu'il y a au moins une balise h2 dans la classe "subtitle"


$this->assertGreaterThan(0, $crawler->filter('h2.subtitle')->count());
// Vrifie qu'il y a exactement 4 balises h2 sur la page
$this->assertCount(4, $crawler->filter('h2'));
// Vrifie que l'entte "Content-Type" vaut "application/json"
$this->assertTrue($client->getResponse()->headers->contains('Content-Type',
'application/json'));
// Vrifie que le contenu retourn correspond la regex
$this->assertRegExp('/foo/', $client->getResponse()->getContent());
// Vrifie que le status de la rponse est 2xx
$this->assertTrue($client->getResponse()->isSuccessful());
// Vrifie que le status de la rponse est 404
$this->assertTrue($client->getResponse()->isNotFound());
// Vrifie un status spcifique
$this->assertEquals(200, $client->getResponse()->getStatusCode());
// Vrifie que la rponse est redirige vers /demo/contact
$this->assertTrue($client->getResponse()->isRedirect('/demo/contact'));
// ou vrifie simplement que la rponse est redirige vers une URL quelconque
$this->assertTrue($client->getResponse()->isRedirect());

Travailler avec le Client Test


Le Client Test simule un client HTTP comme un navigateur et lance des requtes votre application
Symfony2 :
Listing 10-15

1 $crawler = $client->request('GET', '/hello/Fabien');

La mthode request() prend en arguments la mthode HTTP et une URL, et retourne une instance de
Crawler.
Utilisez le Crawler pour trouver des lments DOM dans la Rponse. Ces lments peuvent ainsi tre
utiliss pour cliquer sur des liens et soumettre des formulaires :
Listing 10-16

1
2
3
4
5

$link = $crawler->selectLink('Go elsewhere...')->link();


$crawler = $client->click($link);
$form = $crawler->selectButton('validate')->form();
$crawler = $client->submit($form, array('name' => 'Fabien'));

Les mthodes click() et submit() retournent toutes deux un objet Crawler. Ces mthodes sont le
meilleur moyen de naviguer dans une application, car elles s'occupent de beaucoup de choses pour vous,
comme dtecter la mthode HTTP partir d'un formulaire et vous fournir une bonne API pour uploader
des fichiers.

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 130

Vous en apprendrez davantage sur les objets Link et Form dans la section Crawler ci-dessous.

Mais vous pouvez aussi simuler les soumissions de formulaires et des requtes complexes grce la
mthode request() :
Listing 10-17

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

// Soumettre directement un formulaire (mais utiliser le Crawler est plus simple)


$client->request('POST', '/submit', array('name' => 'Fabien'));
// Soumission de formulaire avec upload de fichier
use Symfony\Component\HttpFoundation\File\UploadedFile;
$photo = new UploadedFile(
'/path/to/photo.jpg',
'photo.jpg',
'image/jpeg',
123
);
// or
$photo = array(
'tmp_name' => '/path/to/photo.jpg',
'name' => 'photo.jpg',
'type' => 'image/jpeg',
'size' => 123,
'error' => UPLOAD_ERR_OK
);
$client->request(
'POST',
'/submit',
array('name' => 'Fabien'),
array('photo' => $photo)
);

// Excute une requte DELETE et passe des entte HTTP


$client->request(
'DELETE',
'/post/12',
array(),
array(),
array('PHP_AUTH_USER' => 'username', 'PHP_AUTH_PW' => 'pa$$word')
);

Enfin, vous pouvez forcer chaque requte tre excute dans son propre processus PHP afin d'viter
quelconques effets secondaires quand vous travaillez avec plusieurs clients dans le mme script :
Listing 10-18

1 $client->insulate();

Naviguer
Le Client supporte de nombreuses oprations qui peuvent tre effectues travers un navigateur rel :
Listing 10-19

1 $client->back();
2 $client->forward();
3 $client->reload();

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 131

4
5 // efface tous les cookies et l'historique
6 $client->restart();

Accder aux Objets Internes


Si vous utilisez le client pour tester votre application, vous pourriez vouloir accder aux objets internes
du client :
Listing 10-20

1 $history
= $client->getHistory();
2 $cookieJar = $client->getCookieJar();

Vous pouvez aussi obtenir les objets lis la dernire requte :


Listing 10-21

1 $request = $client->getRequest();
2 $response = $client->getResponse();
3 $crawler = $client->getCrawler();

Si vos requtes ne sont pas isoles, vous pouvez aussi accder au Container et au Kernel :
Listing 10-22

1 $container = $client->getContainer();
2 $kernel
= $client->getKernel();

Accder au Container
Il est fortement recommand qu'un test fonctionnel teste seulement la Rponse. Mais dans certains cas
rares, vous pourriez vouloir accder quelques objets internes pour crire des assertions. Dans tels cas,
vous pouvez accder au conteneur d'injection de dpendances :
Listing 10-23

1 $container = $client->getContainer();

Notez bien que cela ne fonctionne pas si vous isolez le client ou si vous utilisez une couche HTTP. Pour
une liste des services disponibles dans votre application, utilisez la commande container:debug.
Si les informations que vous avez besoin de vrifier sont disponibles via le profileur, utilisez le.

Accder aux donnes du profileur


Pour chaque requte, vous pouvez activer le profileur Symfony afin de collecter et stocker diverses
informations sur la gestion interne des requtes. Par exemple, le profileur peut tre utilis pour vrifier
qu'une page donne ne dpasse pas un certain nombre de requtes en base de donnes lors de son
chargement.
Pour obtenir le profileur de la dernire requte, utilisez le code suivant:
Listing 10-24

1 // active le profileur pour la requte suivante


2 $client->enableProfiler();
3

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 132

4 $crawler = $client->request('GET', '/profiler');


5
6 // rcupre le profil
7 $profile = $client->getProfile();

Pour des dtails spcifique sur l'utilisation du profileur au sein d'un test, lisez la documentation du
cookbook Comment utiliser le Profiler dans un test fonctionnel.

Redirection
Lorsqu'une requte retourne une rponse redirige, le client ne la suit pas automatiquement. Vous pouvez
examiner la rponse puis forcer une redirection grce la mthode followRedirect():
Listing 10-25

1 $crawler = $client->followRedirect();

Si vous voulez que le client suive automatiquement tous les redirections, vous pouvez le forcer avec la
mthode followRedirects():
Listing 10-26

1 $client->followRedirects();

Le Crawler
Une instance de Crawler est retourne chaque fois que vous effectuez une requte avec le Client. Elle vous
permet de naviguer travers des documents HTML, de slectionner des noeuds, de trouver des liens et
des formulaires.

Traverser
Comme jQuery, le Crawler possde des mthodes lui permettant de naviguer travers le DOM d'un
document HTML/XML. Par exemple, le code suivant trouve tout les lments input[type=submit],
slectionne le dernier de la page et slectionne son lment parent immdiat :
Listing 10-27

1 $newCrawler = $crawler->filter('input[type=submit]')
2
->last()
3
->parents()
4
->first()
5 ;

Beaucoup d'autres mthodes sont galement disponibles :


Method

Description

filter('h1.title')

Noeuds qui correspondent au slecteur CSS

filterXpath('h1')

Noeuds qui correspondent l'expression XPath

eq(1)

Noeud pour l'index spcifi

first()

Premier noeud

last()

Dernier noeud

siblings()

lments de mme niveau (siblings)

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 133

Method

Description

nextAll()

Tous les siblings suivants

previousAll()

Tous les siblings prcdents

parents()

Retourne les noeuds parents

children()

Retourne les noeuds enfants

reduce($lambda)

Noeuds pour lesquels $lambda ne retourne pas false

Puisque chacune de ses mthodes retourne une instance de Crawler, vous pouvez affiner votre slection
de noeuds en enchainant les appels de mthodes :
Listing 10-28

1 $crawler
2
->filter('h1')
3
->reduce(function ($node, $i)
4
{
5
if (!$node->getAttribute('class')) {
6
return false;
7
}
8
})
9
->first();

Utilisez la fonction count() pour avoir le nombre de noeuds contenus dans le Crawler :
count($crawler)

Extraction d'informations
Le Crawler peut extraire des informations des noeuds :
Listing 10-29

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// retourne la valeur de l'attribut du premier noeud


$crawler->attr('class');
// retourne la valeur du noeud pour le premier noeud
$crawler->text();
// extrait un tableau d'attributs pour tous les noeuds (_text retourne
// la valeur du noeud)
// retourne un tableau de chaque lment du crawler, chacun avec les attributs
// value et href
$info = $crawler->extract(array('_text', 'href'));
// excute une lambda pour chaque noeud et retourne un tableau de rsultats
$data = $crawler->each(function ($node, $i)
{
return $node->attr('href');
});

Liens
Vous pouvez slectionner les liens grce aux mthodes ci-dessus ou grce au raccourci trs pratique
selectLink() :

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 134

Listing 10-30

1 $crawler->selectLink('Click here');

Cela slectionne tous les liens qui contiennent le texte donn, ou les images cliquables dont l'attribut alt
contient ce texte. Comme les autres mthodes de filtre, cela retourne un autre objet Crawler.
Une fois que vous avez slectionn un lien, vous avez accs l'objet spcial Link, qui possde des
mthodes utiles et spcifiques aux liens (comme getMethod() et getUri()).Pour cliquer sur un lien,
utilisez la mthode click() du Client et passez la un objet Link :
Listing 10-31

1 $link = $crawler->selectLink('Click here')->link();


2
3 $client->click($link);

Formulaires
Comme pour les liens, vous slectionnez les formulaires l'aide de la mthode selectButton() :
Listing 10-32

1 $buttonCrawlerNode = $crawler->selectButton('submit');

Notez que vous slectionnez les boutons de formulaire et non pas les formulaires eux-mmes, car
un formulaire peut contenir plusieurs boutons; si vous utilisez l'API de traversement, gardez en
mmoire que vous devez chercher un bouton.

La mthode selectButton() peut slectionner des balises button et des balises input de type submit;
elle utilise plusieurs parties du bouton pour les trouver :
La valeur de l'attribut value;
La valeur de l'attribut id ou alt pour les images;
La valeur de l'attribut id ou name pour les balises button.
Lorsque vous avez un Crawler qui reprsente un bouton, appelez la mthode form() pour obtenir une
instance de Form pour le formulaire contenant le noeud du bouton :
Listing 10-33

1 $form = $buttonCrawlerNode->form();

Quand vous appelez la mthode form(), vous pouvez aussi passer un tableau de valeurs de champs qui
rcrit les valeurs par dfaut :
Listing 10-34

1 $form = $buttonCrawlerNode->form(array(
2
'name'
=> 'Fabien',
3
'my_form[subject]' => 'Symfony rocks!',
4 ));

Et si vous voulez simuler une mthode HTTP spcifique pour le formulaire, passez la comme second
argument :
Listing 10-35

1 $form = $buttonCrawlerNode->form(array(), 'DELETE');

Le Client peut soumettre des instances de Form :


Listing 10-36

1 $client->submit($form);

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 135

Les valeurs des champs peuvent aussi tre passes en second argument de la mthode submit() :
Listing 10-37

1 $client->submit($form, array(
2
'name'
=> 'Fabien',
3
'my_form[subject]' => 'Symfony rocks!',
4 ));

Pour les situations plus complexes, utilisez l'instance de Form en tant que tableau pour dfinir la valeur
de chaque champ individuellement :
Listing 10-38

1 // Change la valeur d'un champ


2 $form['name'] = 'Fabien';
3 $form['my_form[subject]'] = 'Symfony rocks!';

Il y a aussi une sympathique API qui permet de manipuler les valeurs des champs selon leur type :
Listing 10-39

1
2
3
4
5
6
7
8

// slectionne une option/radio


$form['country']->select('France');
// Coche une checkbox
$form['like_symfony']->tick();
// Upload un fichier
$form['photo']->upload('/path/to/lucas.jpg');

Vous pouvez obtenir les valeurs qui sont soumises en appelant la mthode getValues() de l'objet
Form. Les fichiers uploads sont disponibles dans un tableau spar retourn par getFiles().
Les mthodes getPhpValues() et getPhpFiles() retournent aussi les valeurs soumises, mais au
format PHP (cela convertit les cls avec la notation entre crochets - ex my_form[subject] - en
tableaux PHP).

Configuration de Test
Le Client utilis par les tests fonctionnels cre un Kernel qui est excut dans un environnement de test.
Puisque Symfony charge le app/config/config_test.yml dans l'environnement de test, vous pouvez
modifier la configuration de votre application spcifiquement pour les tests.
Par exemple, par dfaut, le swiftmailer est configur pour ne pas envoyer de mails en environnement de
test. Vous pouvez le voir sous l'option de configuration swiftmailer :
Listing 10-40

1 # app/config/config_test.yml
2 # ...
3
4 swiftmailer:
5
disable_delivery: true

Vous pouvez aussi utiliser un environnement entirement diffrent, ou surcharger le mode debug par
dfaut (true) en les passant en tant qu'option la mthode createClient() :
Listing 10-41

1 $client = static::createClient(array(
2
'environment' => 'my_test_env',

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 136

3
'debug'
4 ));

=> false,

Si votre application se comporte selon certaines en-ttes HTTP, passez-les en tant que second argument
de createClient() :
Listing 10-42

1 $client = static::createClient(array(), array(


2
'HTTP_HOST'
=> 'en.example.com',
3
'HTTP_USER_AGENT' => 'MySuperBrowser/1.0',
4 ));

Vous pouvez aussi rcrire les en-ttes HTTP par requte (i.e. et non pas pour toutes les requtes) :
Listing 10-43

1 $client->request('GET', '/', array(), array(), array(


2
'HTTP_HOST'
=> 'en.example.com',
3
'HTTP_USER_AGENT' => 'MySuperBrowser/1.0',
4 ));

Le client test est disponible en tant que service dans le conteneur en environnement de test
(ou n'importe o si l'option framework.test est active). Cela signifie que vous pouvez surcharge
entirement le service en cas de besoin.

Configuration PHPUnit
Chaque application possde sa propre configuration PHPUnit, stocke dans le fichier
phpunit.xml.dist. Vous pouvez diter ce fichier pour changer les valeurs par dfaut ou vous pouvez
crer un fichier phpunit.xml pour personnaliser la configuration de votre machine locale.
Stockez le fichier phpunit.xml.dist dans votre gestionnaire de code, et ignorez le fichier
phpunit.xml.

Par dfaut, seulement les tests situs dans des bundles standards sont excuts par la commande
phpunit (standard tant des tests situs dans les rpertoires src/*/Bundle/Tests ou src/*/Bundle/
*Bundle/Tests). Mais vous pouvez aisment ajouter d'autres rpertoires. Par exemple, la configuration
suivante ajoute les tests de bundles tiers installs :
Listing 10-44

1 <!-- hello/phpunit.xml.dist -->


2 <testsuites>
3
<testsuite name="Project Test Suite">
4
<directory>../src/*/*Bundle/Tests</directory>
5
<directory>../src/Acme/Bundle/*Bundle/Tests</directory>
6
</testsuite>
7 </testsuites>

Pour inclure d'autres rpertoires dans la couverture du code, ditez aussi la section <filter> :
Listing 10-45

1 <filter>
2
<whitelist>
3
<directory>../src</directory>
4
<exclude>

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 137

5
<directory>../src/*/*Bundle/Resources</directory>
6
<directory>../src/*/*Bundle/Tests</directory>
7
<directory>../src/Acme/Bundle/*Bundle/Resources</directory>
8
<directory>../src/Acme/Bundle/*Bundle/Tests</directory>
9
</exclude>
10
</whitelist>
11 </filter>

En savoir plus

Le Composant DomCrawler
Le Composant CssSelector
Comment simuler une authentification HTTP dans un Test Fonctionnel
Comment tester les interactions de multiples clients
Comment utiliser le Profiler dans un test fonctionnel
Comment personnaliser le processus de bootstrap avant les tests

PDF brought to you by


generated on January 14, 2015

Chapter 10: Les Tests | 138

Chapter 11

Validation
La validation est une tche trs commune dans les applications web. Les donnes saisies dans les
formulaires doivent tre valides. Les donnes doivent galement tre valides avant d'tre crites dans
une base de donnes ou transmises un service Web.
Symfony2 est livr avec un composant Validator1 qui rend cette tche facile et transparente. Ce
composant est bas sur la spcification JSR3032. Quoi ? Une spcification Java en PHP? Vous avez bien
entendu, mais ce n'est pas aussi terrible que a en a l'air. Regardons comment elle peut tre utilise en
PHP.

Les Bases de la Validation


La meilleure faon de comprendre la validation est de la voir en action. Pour commencer, supposons que
vous avez cr un bon vieil objet PHP que vous avez besoin d'utiliser quelque part dans votre application
:
Listing 11-1

1
2
3
4
5
6
7

// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}

Jusqu' prsent, c'est juste une classe ordinaire qui est utile dans votre application. L'objectif de la
validation est de vous dire si oui ou non les donnes d'un objet sont valides. Pour que cela fonctionne,
vous allez configurer une liste de rgles (appele constraints) que l'objet doit respecter pour tre valide.
Ces rgles peuvent tre spcifies via un certain nombre de formats (YAML, XML, les annotations, ou
PHP).
Par exemple, pour garantir que la proprit $name ne soit pas vide, ajoutez le code suivant :
Listing 11-2

1. https://github.com/symfony/Validator
2. http://jcp.org/en/jsr/detail?id=303

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 139

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\Author:
3
properties:
4
name:
5
- NotBlank: ~

Les proprits protges et prives peuvent galement tre valides, ainsi que les mthodes getter
(voir validator-constraint-targets).

Utiliser le Service validator


Ensuite, pour vraiment valider un objet Author, utilisez la mthode validate du service validator (class
Validator3). Le travail du validator est simple : lire les contraintes (rgles) d'une classe et vrifier si
les donnes sur l'objet satisfont ces contraintes ou non. Si la validation choue, un tableau d'erreurs est
retourn. Prenez cet exemple simple d'une action d'un contrleur :
Listing 11-3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

use Symfony\Component\HttpFoundation\Response;
use Acme\BlogBundle\Entity\Author;
// ...
public function indexAction()
{
$author = new Author();
// ... do something to the $author object
$validator = $this->get('validator');
$errorList = $validator->validate($author);
if (count($errorList) > 0) {
return new Response(print_r($errorList, true));
} else {
return new Response('The author is valid! Yes!');
}
}

Si la proprit $name est vide, vous allez voir le message d'erreur suivant :
Listing 11-4

1 Acme\BlogBundle\Author.name:
2
This value should not be blank

Si vous insrez une valeur dans la proprit name, le message de succs va apparatre.
La plupart du temps, vous n'aurez pas interagir directement avec le service validator ou
besoin de vous inquiter concernant l'affichage des erreurs. La plupart du temps, vous allez
utiliser la validation indirectement lors de la soumission des donnes du formulaire. Pour plus
d'informations, consultez-le Validation et Formulaires.

Vous pouvez aussi passer une collection d'erreurs un template.


Listing 11-5

3. http://api.symfony.com/2.6/Symfony/Component/Validator/Validator.html

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 140

1 if (count($errorList) > 0) {
2
return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
3
'errorList' => $errorList,
4
));
5 } else {
6
// ...
7 }

A l'intrieur d'un template, vous pouvez afficher la liste des erreurs comme vous voulez :
Listing 11-6

1
2
3
4
5
6
7
8

{# src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}
<h3>The author has the following errors</h3>
<ul>
{% for error in errorList %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>

Chaque erreur de validation (appele une violation de contrainte ), est reprsente par un objet
ConstraintViolation4.

Validation et Formulaires
Le service validator peut tre utilis tout moment pour valider n'importe quel objet. En ralit,
vous travaillerez le plus souvent avec le validator indirectement lorsque vous utilisez les formulaires.
La bibliothque de formulaires de Symfony utilise le service validator en interne pour valider l'objet
aprs que les valeurs ont t soumises. Les violations de contraintes sur l'objet sont converties en objets
FieldError qui peuvent tre facilement affichs dans votre formulaire. Le processus de soumission d'un
formulaire typique ressemble au code suivant:
Listing 11-7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// ...
use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
$author = new Author();
$form = $this->createForm(new AuthorType(), $author);
$form->handleRequest($request);
if ($form->isValid()) {
// the validation passed, do something with the $author object
return $this->redirect($this->generateUrl(...));
}
return $this->render('BlogBundle:Author:form.html.twig', array(
'form' => $form->createView(),

4. http://api.symfony.com/2.6/Symfony/Component/Validator/ConstraintViolation.html

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 141

21
22 }

));

Cet exemple utilise une classe de formulaire AuthorType, qui n'est pas montre ici.

Pour plus d'informations, voir le chapitre Forms.

Configuration
Le validateur Symfony2 est activ par dfaut, mais vous devez activer explicitement les annotations, si
vous voulez les utiliser pour spcifier vos contraintes :
Listing 11-8

1 # app/config/config.yml
2 framework:
3
validation: { enable_annotations: true }

Contraintes
Le validator est conu pour valider des objets selon des contraintes (rgles). Afin de valider un objet, il
suffit d'associer une ou plusieurs contraintes sa classe et ensuite de le passer au service validator.
Dans les coulisses, une contrainte est simplement un objet PHP qui dclare une rgle. Dans la vraie vie,
une contrainte pourrait tre : Le gteau ne doit pas tre brl . En Symfony2, les contraintes sont
similaires : ce sont des garanties qu'une condition est vraie. Pour une valeur donne, une contrainte vous
dira si cette valeur obit aux rgles ou non.

Contraintes supportes
Symfony2 est fourni avec un grand nombre des contraintes les plus utiles habituellement.

Contraintes de bases
Voici les contraintes de base : utilisez les afin de vrifier des donnes basiques propos de la valeur d'une
proprit ou de la valeur retourne par une mthode de votre objet.

NotBlank
Blank
NotNull
Null
True
False
Type

Contraintes sur les chanes de caractres


Email
Length
Url
PDF brought to you by
generated on January 14, 2015

Chapter 11: Validation | 142

Regex
Ip

Contraintes sur les nombres


Range

Contraintes comparatives

EqualTo
NotEqualTo
IdenticalTo
NotIdenticalTo
LessThan
LessThanOrEqual
GreaterThan
GreaterThanOrEqual

Contraintes sur les dates


Date
DateTime
Time

Contraintes sur les collections

Choice
Collection
Count
UniqueEntity
Language
Locale
Country

Contraintes sur les fichiers


File
Image

Contraintes sur la finance et autres numros

CardScheme
Luhn
Iban
Isbn
Issn

Autres Contraintes

Callback
All
UserPassword
Valid

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 143

Vous pouvez aussi crer vos propres contraintes. Ce sujet est abord dans l'article Comment crer une
Contrainte de Validation Personnalise du cookbook.

Configuration de contraintes
Certaines contraintes, comme NotBlank, sont simples alors que d'autres, comme la contrainte Choice ont
plusieurs options de configuration disponibles. Supposons que la classe Author a une autre proprit,
gender qui peut prendre comme valeur homme ou femme :
Listing 11-9

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\Author:
3
properties:
4
gender:
5
- Choice: { choices: [homme, femme], message: Choose a valid gender. }

Les options d'une contrainte peuvent toujours tre passes en tant que tableau. Certaines contraintes,
cependant, vous permettent galement de passer la valeur d'une option par dfaut au lieu du tableau.
Dans le cas de la contrainte Choice, les options de choices peuvent tre spcifies de cette manire.
Listing 11-10

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\Author:
3
properties:
4
gender:
5
- Choice: [homme, femme]

Ceci a pour unique but de rendre la configuration de l'option la plus probable plus courte et plus rapide.
Si jamais vous n'tes pas certain de la faon dont spcifier une option, soit vrifiez la documentation de
l'API pour la contrainte concerne, soit jouez la scurit en passant toujours dans un tableau d'options (la
premire mthode indique ci-dessus).

Traduction des messages de contrainte


Pour des informations sur la traduction des messages de contrainte, allez voir Traduire les messages de
contraintes.

Objectifs des contraintes


Les contraintes peuvent tre appliques une proprit de classe (par ex. name) ou une mthode publique
getter (par ex. getFullName). La premire mthode est la plus commune et facile utiliser, mais la
seconde vous permet de spcifier des rgles de validation plus complexes.
Proprits
Valider des proprits de classe est la technique de validation la plus basique. Symfony2 vous permet
de valider des proprits prives, protges ou publiques. Le prochain exemple vous montre comment
configurer la proprit $firstName d'une classe Author pour avoir au moins 3 caractres.
Listing 11-11

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\Author:
3
properties:
4
firstName:
5
- NotBlank: ~

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 144

6
7

- MinLength:
min: 3

Getters
Les contraintes peuvent galement tre appliques la valeur de retour d'une mthode. Symfony2 vous
permet d'ajouter une contrainte toute mthode publique dont le nom commence par get ou is .
Dans ce guide, ces deux types de mthodes sont dsignes comme getters .
L'avantage de cette technique est qu'elle vous permet de valider votre objet dynamiquement. Par exemple,
supposons que vous voulez vous assurer qu'un champ mot de passe ne correspond pas au prnom de
l'utilisateur (pour des raisons de scurit). Vous pouvez le faire en crant une mthode isPasswordLegal,
puis en spcifiant que cette mthode doit retourner true :
Listing 11-12

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\Author:
3
getters:
4
passwordLegal:
5
- "True": { message: "Le mot de passe et le prnom doivent tre diffrents" }

Maintenant, crez la mthode isPasswordLegal(), et faites ce dont vous avez besoin:


Listing 11-13

1 public function isPasswordLegal()


2 {
3
return ($this->firstName != $this->password);
4 }

Les plus perspicaces d'entre vous auront remarqu que le prfixe du getter ( get ou is ) est
omis dans le mapping. Cela vous permet de dplacer la contrainte sur une proprit du mme nom
plus tard (ou vice versa) sans changer votre logique de validation.

Classes
Certaines contraintes s'appliquent toute la classe qui est valide. Par exemple, la contrainte Callback
est une contrainte gnrique qui s'applique la classe elle-mme. Lorsque cette classe est valide, les
mthodes spcifies par cette contrainte sont simplement excutes pour personnaliser encore plus la
validation

Groupes de Validation
Jusqu'ici, vous avez t en mesure d'ajouter des contraintes une classe et demander si oui ou non
cette classe satisfait toutes les contraintes dfinies. Dans certains cas, cependant, vous aurez besoin de
valider un objet en ne prenant en compte que certaines des contraintes de la classe. Pour ce faire, vous
pouvez organiser chaque contrainte en un ou plusieurs groupes de validation , et ensuite appliquer la
validation sur un groupe de contraintes seulement.
Par exemple, supposons que vous avez une classe User, qui est utilise la fois quand un utilisateur
s'enregistre et quand un utilisateur met jour son profil plus tard :
Listing 11-14

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 145

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\User:
3
properties:
4
email:
5
- Email: { groups: [registration] }
6
password:
7
- NotBlank: { groups: [registration] }
8
- Length: { min: 7, groups: [registration] }
9
city:
10
- Length:
11
min: 2

Avec cette configuration, il y a deux groupes de validation :


Default - contient les contraintes non affectes tout autre groupe ;
registration - contient les contraintes sur les champs email and password seulement.
Pour dire au validateur d'utiliser un groupe spcifique, passer un ou plusieurs noms de groupe comme
deuxime argument de la mthode validate()
Listing 11-15

1 $errorList = $validator->validate($author, array('registration'));

Bien sr, vous travaillerez bien souvent indirectement avec la validation via la bibliothque de formulaire.
Pour plus d'informations sur la faon d'utiliser les groupes de validation l'intrieur des formulaires, voir
Les Groupes de Validation.

Valider des valeurs et des tableaux


Jusqu'ici, vous avez vu comment valider des objets entiers. Mais souvent, vous voudrez juste valider une
simple valeur, comme par exemple vrifier qu'une chane de caractres est une adresse email valide. C'est
en fait trs facile faire. Dans un contrleur, a ressemble ceci:
Listing 11-16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// Ajouter ceci en haut de votre classe


use Symfony\Component\Validator\Constraints\Email;
public function addEmailAction($email)
{
$emailConstraint = new Email();
// toutes les "options" de contrainte sont dfinies comme suit
$emailConstraint->message = 'Invalid email address';

// utiliser le validator pour valider la valeur


$errorList = $this->get('validator')->validateValue($email, $emailConstraint);
if (count($errorList) == 0) {
// c'est une adresse valide, faire quelque chose
} else {
// ce n'est *pas* une adresse valide
$errorMessage = $errorList[0]->getMessage()

// faire quelque chose avec l'erreur


}

// ...
}

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 146

En appelant validateValue du validator, vous pouvez passer une valeur brute et l'objet contrainte dont
vous voulez valider la valeur. Une liste complte des contraintes disponibles - ainsi que le nom de classe
complet de chaque contrainte - est disponible dans le chapitre contraintes.
La mthode validateValue retourne un objet ConstraintViolationList5 qui se comporte comme un
tableau d'erreurs. Chaque erreur de la collection est un objet ConstraintViolation6, qui contient le
message d'erreur dans sa mthode getMessage.

Le mot de la fin
Le validator de Symfony2 est un outil puissant qui peut tre un atout pour garantir que les donnes
de n'importe quel objet sont valides . La puissance de la validation rside dans les contraintes ,
qui sont des rgles que vous pouvez appliquer aux proprits ou aux mthodes getter de votre objet. Et
tandis que vous utiliserez plus communment le systme de validation indirectement lors de l'utilisation
des formulaires, n'oubliez pas qu'il peut tre utilis partout pour valider n'importe quel objet.

En savoir plus avec le Cookbook


Comment crer une Contrainte de Validation Personnalise

5. http://api.symfony.com/2.6/Symfony/Component/Validator/ConstraintViolationList.html
6. http://api.symfony.com/2.6/Symfony/Component/Validator/ConstraintViolation.html

PDF brought to you by


generated on January 14, 2015

Chapter 11: Validation | 147

Chapter 12

Formulaires
Intragir avec les formulaires HTML est l'une des tches les plus habituelles - et stimulantes - pour un
dveloppeur web. Symfony2 intgre un composant Form qui rend cette intraction trs facile. Dans
ce chapitre, vous allez construire un formulaire complexe depuis la base, tout en apprenant au fur et
mesure les caractristiques les plus importantes de la bibliothque des formulaires.
Le composant formulaire de Symfony est une bibliothque autonome qui peut tre utilise en
dehors des projets Symfony2. Pour plus d'informations, voir le Composant Formulaire Symfony21
sur Github.

Crer un formulaire simple


Supposez que vous construisiez une application todo list (en franais : liste de choses faire )
simple qui doit afficher des tches . Parce que vos utilisateurs devront diter et crer des tches, vous
allez avoir besoin de construire des formulaires. Mais avant de commencer, concentrez-vous d'abord sur
la classe gnrique Task qui reprsente et stocke les donnes pour une tche:
Listing 12-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// src/Acme/TaskBundle/Entity/Task.php
namespace Acme\TaskBundle\Entity;
class Task
{
protected $task;
protected $dueDate;
public function getTask()
{
return $this->task;
}
public function setTask($task)

1. https://github.com/symfony/Form

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 148

15
16
17
18
19
20
21
22
23
24
25
26
27 }

{
$this->task = $task;
}
public function getDueDate()
{
return $this->dueDate;
}
public function setDueDate(\DateTime $dueDate = null)
{
$this->dueDate = $dueDate;
}

Si vous codez en mme temps que vous lisez cet exemple, crez en premier le bundle
AcmeTaskBundle en excutant la commande suivante (et en acceptant toutes les options par
dfaut):
Listing 12-2

1 $ php app/console generate:bundle --namespace=Acme/TaskBundle

Cette classe est un bon vieil objet PHP parce que, jusqu'ici, elle n'a rien voir avec Symfony ou un
quelconque autre bibliothque. C'est tout simplement un objet PHP normal qui rpond directement
un besoin dans votre application (c-a-d le besoin de reprsenter une tche dans votre application). Bien
sr, la fin de ce chapitre, vous serez capable de soumettre des donnes une instance de Task (via un
formulaire HTML), de valider ses donnes, et de les persister dans la base de donnes.

Construire le Formulaire
Maintenant que vous avez cr une classe Task, la prochaine tape est de crer et d'afficher le formulaire
HTML. Dans Symfony2, cela se fait en construisant un objet formulaire qui est ensuite affich dans un
template. Pour l'instant, tout ceci peut tre effectu dans un contrleur:
Listing 12-3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
// cre une tche et lui donne quelques donnes par dfaut pour cet exemple
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 149

23
24
25
26
27 }

return $this->render('AcmeTaskBundle:Default:new.html.twig', array(


'form' => $form->createView(),
));
}

Cet exemple vous montre comment construire votre formulaire directement depuis le contrleur.
Plus tard, dans la section Crer des Classes de Formulaire , vous apprendrez construire votre
formulaire dans une classe autonome, ce qui est recommand, car vos formulaires deviendront
ainsi rutilisables.

Crer un formulaire ncessite relativement peu de code, car les objets formulaires de Symfony2 sont
construits avec un constructeur de formulaire ( form builder ). Le principe du constructeur de
formulaire est de vous permettre d'crire des conteneurs de formulaire simples, et de le laisser prendre
en charge la plus grosse partie du travail qu'est la construction du formulaire.
Dans cet exemple, vous avez ajout deux champs votre formulaire - task et dueDate - correspondants
aux proprits task et dueDate de votre classe Task. Vous avez aussi assign chaque champ un type
(par exemple : text, date), qui, entre autres, dtermine quelle(s) balise(s) HTML est affiche pour ce
champ. Enfin, vous avez ajout un bouton submit pour envoyer le formulaire sur le serveur.
New in version 2.3: Le support des boutons submit est une nouveaut de Symfony 2.3. Avant
cela, vous deviez ajouter vous mme les boutons au formulaire HTML.

Symfony2 est livr avec de nombreux types prdfinis qui seront prsents rapidement plus tard (voir
Types de champ intgrs).

Affichage du Formulaire
Maintenant que le formulaire a t cr, la prochaine tape est de l'afficher. Ceci est fait en passant un
objet spcial vue de formulaire votre template (notez le $form->createView() dans le contrleur
ci-dessus) et en utilisant un ensemble de fonctions d'aide (helpers) pour les formulaires :
Listing 12-4

1 {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
2
3 {{ form(form) }}

Cet exemple suppose que vous soumettez le formulaire grce une requte POST , et la mme
URL que celle qui l'a affich. Vous apprendrez plus tard comment changer la mthode de la requte
et l'URL cible du formulaire.

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 150

C'est tout ! En affichant form(form), chaque champ du formulaire est affich, avec un label et un message
d'erreur (si erreur il y a). La fonction form affiche tout dans une balise HTML form. Aussi facile que
cela soit, ce n'est pas (encore) trs flexible. Habituellement, vous voudrez afficher chaque champ du
formulaire individuellement afin de pouvoir contrler ce quoi le formulaire ressemble. Vous apprendrez
comment faire cela dans la section Afficher un Formulaire dans un Template.
Avant de continuer, notez comment le champ task affich possde la valeur de la proprit task de
l'objet $task (c-a-d Write a blog post ). C'est le premier travail d'un formulaire : de prendre les donnes
d'un objet et de les traduire dans un format adapt pour tre affich dans un formulaire HTML.
Le systme de formulaire est assez intelligent pour accder la valeur de la proprit protge
task via les mthodes getTask() et setTask() de la classe Task. A moins qu'une proprit soit
publique, elle doit avoir une mthode getter et un setter afin que le composant formulaire
puisse rcuprer et assigner des donnes cette proprit. Pour une proprit boolenne, vous
pouvez utiliser une mthode isser ou hasser (par exemple : isPublished() ou
hasReminder()) la place d'un getter (par exemple : getPublished() ou getReminder()).

Grer la Soumission des Formulaires


Le second travail d'un formulaire est de transmettre les donnes soumises par l'utilisateur aux proprits
d'un objet. Pour raliser cela, les donnes soumises par l'utilisateur doivent tre lies au formulaire.
Ajoutez la fonctionnalit suivante votre contrleur:
Listing 12-5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// ...
use Symfony\Component\HttpFoundation\Request;
public function newAction(Request $request)
{
// just setup a fresh $task object (remove the dummy data)
$task = new Task();
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// fait quelque chose comme sauvegarder la tche dans la bdd
return $this->redirect($this->generateUrl('task_success'));
}

// ...
}

New in version 2.3: La mthode handleRequest()2 a t ajoute dans Symfony 2.3. Avant cela, la
$request tait passe la mthode submit, une stratgie qui est dprcie et qui sera supprime
dans Symfony 3.0. Pour plus de dtails sur cette mthode, lisez Passer une requte Form::submit()
(deprci).

2. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#handleRequest()

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 151

Ce contrleur suit un pattern commun dans la manire de grer les formulaires, et a trois scnarios
possibles :
1. Lors du chargement initial de la page dans votre navigateur, le formulaire est simplement cr
et affich. handleRequest()3 dtermine que le formulaire n'a pas t soumis et ne fait rien.
isValid()4 retourne false si le formulaire n'a pas t soumis.
2. Lorsque l'utilisateur soumet le formulaire, handleRequest()5 s'en rend compte et crit
immdiatement les donnes soumises dans les proprits task et dueDate de l'objet $task.
Ensuite, cet objet est valid. S'il est invalide (la validation est aborde dans la section suivante),
isValid()6 retourne encore false et le formulaire est affich de nouveau avec toutes ses erreurs
de validation.
Vous pouvez utiliser la mthode isSubmitted()7 pour vrifier si un formulaire a t
soumis ou non, indpendemment du fait que les donnes soumises soient valides ou non.

3. Lorsque l'utilisateur soumet le formulaire avec des donnes valides, les donnes soumises sont
toujours crites dans l'objet, mais cette fois, isValid()8 retourne true. Vous avez alors la
possibilit d'effectuer certaines actions qui utilisent l'objet $task (comme le persister en base de
donnes) avant de rediriger l'utilisateur vers une autre page (par exemple une page merci ou
a a march ! ).
Rediriger un utilisateur aprs une soumission de formulaire russie empche l'utilisateur de
pouvoir rafraichir la page et de resoumettre les donnes.

Grer la Soumission des Formulaires ayant Plusieurs Boutons


New in version 2.3: Le support des boutons dans les formulaires est une nouveaut de Symfony 2.3.

Lorsque votre formulaire contient plus d'un bouton submit, vous devrez vrifier quel bouton a t utilis
pour adapter les actions effectuer dans votre contrleur. Ajoutons un second bouton avec l'intitul
Enregistrer et nouveau (save and add) notre formulaire:
Listing 12-6

1 $form = $this->createFormBuilder($task)
2
->add('task', 'text')
3
->add('dueDate', 'date')
4
->add('save', 'submit')
5
->add('saveAndAdd', 'submit')
6
->getForm();

Dans votre contrleur, utilisez la mthode isClicked()9 du bouton pour vrifier si un clic sur le bouton
Enregistrer et nouveau a t fait:

3. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#handleRequest()
4. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#isValid()
5. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#handleRequest()
6. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#isValid()
7. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#isSubmitted()
8. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#isValid()
9. http://api.symfony.com/2.6/Symfony/Component/Form/ClickableInterface.html#isClicked()

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 152

Listing 12-7

1 if ($form->isValid()) {
2
// fait quelque chose comme sauvegarder la tche dans la bdd
3
4
$nextAction = $form->get('saveAndAdd')->isClicked()
5
? 'task_new'
6
: 'task_success';
7
8
return $this->redirect($this->generateUrl($nextAction));
9 }

Validation de formulaire
Dans la section prcdente, vous avez appris comment un formulaire peut tre soumis avec des donnes
valides ou non. Dans Symfony2, la validation est applique l'objet sous-jacent (par exemple : Task). En
d'autres termes, la question n'est pas de savoir si le formulaire est valide, mais plutt de savoir si l'objet
$task est valide ou non aprs que le formulaire lui a appliqu les donnes. Appeler $form->isValid()
est un raccourci qui demande l'objet $task si oui ou non il possde des donnes valides.
La validation est effectue en ajoutant un ensemble de rgles (appeles contraintes) une classe. Pour
voir cela en action, ajoutez des contraintes de validation afin que le champ task ne puisse pas tre vide et
que le champ dueDate ne puisse pas tre vide et qu'il doive tre un objet DateTime valide.
Listing 12-8

1 # Acme/TaskBundle/Resources/config/validation.yml
2 Acme\TaskBundle\Entity\Task:
3
properties:
4
task:
5
- NotBlank: ~
6
dueDate:
7
- NotBlank: ~
8
- Type: \DateTime

C'est tout ! Si vous soumettez le formulaire nouveau avec des donnes non valides, vous allez voir les
erreurs correspondantes affiches avec le formulaire.

Validation HTML5
Grce HTML5, de nombreux navigateurs peuvent nativement forcer certaines contraintes de
validation ct client. La validation la plus commune est active en affichant un attribut required
sur les champs qui sont requis. Pour les navigateurs qui supportent HTML5, cela entrainera
l'affichage d'un message natif du navigateur si l'utilisateur essaye de soumettre le formulaire avec
ce champ vide.
Les formulaires gnrs profitent pleinement de cette nouvelle fonctionnalit en ajoutant des
attributs HTML qui dclenchent la validation. La validation ct client, nanmoins, peut tre
dsactive en ajoutant l'attribut novalidate la balise form ou formnovalidate la balise submit.
Ceci est particulirement utile quand vous voulez tester vos contraintes de validation ct serveur,
mais que vous en tes empch par le formulaire de votre navigateur, par exemple, quand vous
soumettez des champs vides.

La validation est une fonctionnalit trs puissante de Symfony2 et possde son propre chapitre.

Les Groupes de Validation

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 153

Si vous n'utilisez pas les groupes de validation, alors vous pouvez sauter cette section.

Si votre objet profite des avantages des groupes de validation, vous aurez besoin de spcifier quel(s)
groupe(s) de validation votre formulaire doit utiliser:
Listing 12-9

1 $form = $this->createFormBuilder($users, array(


2
'validation_groups' => array('registration'),
3 ))->add(...);

Si vous crez des classes de formulaire (une bonne pratique), alors vous devrez ajouter ce qui suit la
mthode setDefaultOptions():
Listing 12-10

1
2
3
4
5
6
7
8

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('registration'),
));
}

Dans les deux cas, le groupe de validation registration uniquement sera utilis pour valider l'objet sousjacent.

Dsactiver la validation
New in version 2.3: La possibilit de dfinir validation_groups false a t ajoute dans Symfony
2.3, bien que la dfinir comme un tableau vide revient au mme dans les versions prcdentes.

Parfois, il peut tre utile de supprimer compltement la validation d'un formulaire. Pour cela, vous
pouvez viter l'appel la mthode isValid()10 dans votre contrleur. Si cela n'est pas possible, vous
pouvez galement dfinir l'option validation_groups false ou en tant que tableau vide:
Listing 12-11

1
2
3
4
5
6
7
8

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => false,
));
}

Notez que lorsque vous faites ceci, le formulaire excutera toujours des vrifications de bases, comme
par exemple virifier si un fichier est trop gros pour tre upload, ou si des champs qui n'existent pas
sont soumis. Si vous voulez supprimer compltement la validation, supprimez l'appel la mthode
isValid()11 dans votre contrleur.

10. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#isValid()
11. http://api.symfony.com/2.6/Symfony/Component/Form/FormInterface.html#isValid()

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 154

Groupes bass sur les donnes soumises


Si vous avez besoin de plus de logique pour dterminer les groupes de validation (c'est--dire en vous
basant sur les donnes), vous pouvez dfinir l'option validation_groups comme un tableau callback:
Listing 12-12

1
2
3
4
5
6
7
8
9
10
11

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array(
'Acme\AcmeBundle\Entity\Client',
'determineValidationGroups',
),
));
}

Cela appellera la mthode statique determineValidationGroups() de la classe Client aprs que le


formulaire est soumis, mais avant que la validation ne soit faite. L'objet Form est pass comme argument
cette mthode (regardez l'exemple suivant). Vous pouvez aussi dfinir une logique entire en utilisant
une Closure:
Listing 12-13

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) {
$data = $form->getData();
if (Entity\Client::TYPE_PERSON == $data->getType()) {
return array('person');
} else {
return array('company');
}
},
));
}

Groupes bass sur les clics de boutons


New in version 2.3: Le support des boutons dans les formulaires est une nouveaut de Symfony 2.3.

Lorsque votre formulaire contient plusieurs boutons submit, vous pouvez changer de groupe de
validation selon le bouton qui a t utilis pour soumettre le formulaire. Par exemple, considrez un
formulaire d'assistance qui vous permet d'aller une tape suivante, ou de revenir une tape prcdente.
Supposez galement que lorsque vous retournez l'tape prcdente, les donnes du formulaires doivent
tre enregistres mais pas valides.
Premirement, vous avez besoin d'ajouter les deux boutons au formulaire:
Listing 12-14

1 $form = $this->createFormBuilder($task)
2
// ...

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 155

3
4
5

->add('nextStep', 'submit')
->add('previousStep', 'submit')
->getForm();

Ensuite, configurez le bouton qui renvoie l'tape prcdente pour qu'il excute des groupes de
validation spcifiques. Dans cet exemple, vous voulez qu'il supprime la validation, donc vous devez
dfinir ses options validation_groups false:
Listing 12-15

1 $form = $this->createFormBuilder($task)
2
// ...
3
->add('previousStep', 'submit', array(
4
'validation_groups' => false,
5
))
6
->getForm();

Maintenant, le formulaire ne vrifiera pas les contraintes de validation. Mais il effectuera toujours des
vrifications d'intgrit de base, comme vrifier si un fichier est trop gros pour tre upload ou si vous
tentez de soumettre un texte dans un champ cens contenir un nombre.

Types de champ intgrs


Symfony est fourni par dfaut avec un grand nombre de types de champ qui couvrent la plupart des
champs de formulaire existants et des types de donnes que vous pourrez rencontrer :

Champs de type Texte

text
textarea
email
integer
money
number
password
percent
search
url

Champs de type Choice

choice
entity
country
language
locale
timezone
currency

Champs de type Date et Time


date
datetime
time
PDF brought to you by
generated on January 14, 2015

Chapter 12: Formulaires | 156

birthday

Autres champs
checkbox
file
radio

Champs de type Groupe


collection
repeated

Champs cachs
hidden

Boutons
button
reset
submit

Champs de base
form
Vous pouvez aussi crer vos propres types de champ personnaliss. Ce sujet est couvert par l'article du
cookbook Comment Crer un Type de Champ de Formulaire Personnalis .

Options des types de champ


Chaque type de champ possde un nombre d'options qui peuvent tre utilises pour le configurer. Par
exemple, le champ dueDate est affich par dfaut comme 3 champs select. Cependant, le champ date
peut tre configur pour tre affich en tant qu'un unique champ texte (o l'utilisateur doit entrer la date
manuellement comme une chane de caractres):
Listing 12-16

1 ->add('dueDate', 'date', array('widget' => 'single_text'))

Chaque type de champ a un certain nombre d'options diffrentes qui peuvent lui tre passes. Beaucoup
d'entre elles sont spcifiques chacun d'entres-eux et vous pouvez trouver ces dtails dans la
documentation de chaque type.

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 157

L'option required
La plus commune des options est l'option required, qui peut tre applique tous les champs.
Par dfaut, cette dernire est dfinie true, signifiant que les navigateurs supportant HTML5
vont appliquer la validation ct client si le champ est laiss vide. Si vous ne souhaitez pas
ce comportement, vous pouvez soit dfinir l'option required de vos champs false, ou soit
dsactiver la validation HTML5.
Notez aussi que dfinir l'option required true ne crera aucune validation ct serveur. En
d'autres termes, si un utilisateur soumet une valeur vide pour le champ (soit avec un vieux
navigateur ou via un service web, par exemple), cette valeur sera accepte comme valide moins
que vous n'utilisiez la contrainte de validation de Symfony NotBlank ou NotNull.
En d'autres termes, l'option required est cool , mais une relle validation ct serveur devrait
toujours tre mise en place.

L'option label
Le label d'un champ de formulaire peut tre dfini grce l'option label, qui s'applique
n'importe quel champ:
Listing 12-17

1 ->add('dueDate', 'date', array(


2
'widget' => 'single_text',
3
'label' => 'Due Date',
4 ))

Le label d'un champ peut aussi tre dfini dans le template lorsque vous affichez le formulaire, voir
ci-dessous.

Prdiction de Type de Champ


Maintenant que vous avez ajout les mtadonnes de validation la classe Task, Symfony en sait dj un
peu plus propos de vos champs. Si vous l'autorisez, Symfony peut prdire le type de vos champs et
les mettre en place pour vous. Dans cet exemple, Symfony peut deviner depuis les rgles de validation
que le champ task est un champ texte classique et que dueDate est un champ date:
Listing 12-18

1 public function newAction()


2 {
3
$task = new Task();
4
5
$form = $this->createFormBuilder($task)
6
->add('task')
7
->add('dueDate', null, array('widget' => 'single_text'))
8
->getForm();
9 }

La prdiction est active lorsque vous omettez le second argument de la mthode add() (ou si vous lui
passez null). Si vous passez un tableau d'options en tant que troisime argument (comme pour dueDate
ci-dessus), ces options sont appliques au champ prdit.

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 158

Si votre formulaire utilise un groupe de validation spcifique, le prdicteur de type de champ


continuera toujours considrer toutes les contraintes de validation lorsqu'il essaie de deviner les
types de champ (incluant les contraintes qui ne font pas partie du ou des groupes qui sont utiliss).

Prdiction des options de type de champ


En plus de pouvoir prdire le type d'un champ, Symfony peut aussi essayer de deviner les valeurs
correctes d'un certain nombre d'options de champ.
Lorsque ces options sont dfinies, le champ sera affich avec des attributs HTML spcifiques qui
permettent la validation du champ ct client. Cependant, cela ne gnre pas l'quivalent ct
serveur (ex Assert\Length). Et puisque vous aurez besoin d'ajouter la validation ct serveur
manuellement, ces options peuvent tre dduites de cette information.

required : L'option required peut tre devine grce aux rgles de validation (est-ce que
le champ est NotBlank ou NotNull?) ou grce aux mtadonnes de Doctrine (est-ce que le
champ est nullable?). Ceci est trs utile car votre validation ct client va automatiquement
correspondre vos rgles de validation.
max_length : Si le champ est de type texte, alors l'option max_length peut tre devine grce
aux contraintes de validation (si Length ou Range sont utilises) ou grce aux mtadonnes de
Doctrine (via la longueur du champ).
Ces options de champ sont uniquement devines si vous utilisez Symfony pour prdire le type des
champs (c-a-d en omettant ou en passant null en tant que deuxime argument de la mthode
add()).

Si vous voulez changer une des valeurs prdites, vous pouvez la surcharger en passant l'option au tableau
des options de champ
Listing 12-19

1 ->add('task', null, array('max_length' => 4))

Afficher un Formulaire dans un Template


Jusqu'ii, vous avez vu comment un formulaire entier peut tre affich avec seulement une ligne de code.
Bien sr, vous aurez probablement besoin de bien plus de flexibilit :
Listing 12-20

1 {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
2 {{ form_start(form) }}
3
{{ form_errors(form) }}
4
5
{{ form_row(form.task) }}
6
{{ form_row(form.dueDate) }}
7
8
<input type="submit" />
9 {{ form_end(form) }}

Jetons un coup d'oeil chaque partie :


form_start(form) - Affiche la balise d'ouverture form.

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 159

form_errors(form) - Affiche les erreurs globales du formulaire (les erreurs spcifiques


chaque champ seront affiches ct des champs);
form_row(form.dueDate) - Affiche le label, les erreurs ventuelles et le widget HTML d'un
champ donn (ex dueDate) dans un lment div (par dfaut);
form_end() - Affiche la balise de fermeture du formulaire ainsi que tous les champs qui n'ont
pas encore t affichs. C'est utile pour afficher les champs cachs et pour profiter de la
protection CSRF automatique.
La plus grande partie du travail est effectue par la fonction d'aide form_row, qui rend le label, les
erreurs et le widget HTML de chaque champ dans une balise div par dfaut. Dans la section Habillage
de Formulaire ( Theming ), vous apprendrez comment le rendu de form_row peut tre personnalis
diffrents niveaux.
Vous pouvez accder la donne courante de votre formulaire via form.vars.value:
Listing 12-21

1 {{ form.vars.value.task }}

Afficher chaque Champ la Main


La fonction form_row est gniale, car, grce elle, vous pouvez afficher trs rapidement chaque champ de
votre formulaire (et les balises utilises pour ce champ peuvent tre personnalises aussi). Mais comme la
vie n'est pas toujours simple, vous pouvez aussi afficher chaque champ entirement la main. Le rsultat
dcrit ci-dessous revient au mme que lorsque vous avez utilis la fonction form_row :
Listing 12-22

1 {{ form_start(form) }}
2
{{ form_errors(form) }}
3
4
<div>
5
{{ form_label(form.task) }}
6
{{ form_errors(form.task) }}
7
{{ form_widget(form.task) }}
8
</div>
9
10
<div>
11
{{ form_label(form.dueDate) }}
12
{{ form_errors(form.dueDate) }}
13
{{ form_widget(form.dueDate) }}
14
</div>
15
16 <input type="submit" />
17
18 {{ form_end(form) }}

Si le label autognr pour un champ n'est pas tout fait correct, vous pouvez le spcifier explicitement :
Listing 12-23

1 {{ form_label(form.task, 'Task Description') }}

Certains types de champ ont des options d'affichage supplmentaires qui peuvent tre passes au widget.
Ces options sont documentes avec chaque type, mais l'option attr est commune tous les type et vous
permet de modifier les attributs d'un lment de formulaire. Ce qui suit ajouterait la classe task_field
au champ texte affich :
Listing 12-24

1 {{ form_widget(form.task, {'attr': {'class': 'task_field'}}) }}

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 160

Si vous avez besoin d'afficher des champs du formulaire la main alors vous pouvez utiliser
individuellement les valeurs des champs comme id, name et label. Par exemple, pour obtenir l'id :
Listing 12-25

1 {{ form.task.vars.id }}

Pour obtenir la valeur utilise pour l'attribut name du champ de formulaire, vous devez utiliser la valeur
full_name :
Listing 12-26

1 {{ form.task.vars.full_name }}

Fonctions de Rfrence des Templates Twig


Si vous utilisez Twig, un manuel de rfrence complet des fonctions d'affichage de formulaire est mis
votre disposition. Lisez-le afin de tout connatre sur les fonctions d'aide disponibles ainsi que les options
qui peuvent tre utilises pour chacune d'elles.

Changer les attributs Action et Method d'un formulaire


Jusqu'ici, la fonction form_start() a t utilise pour afficher la balise d'ouverture du formulaire et nous
avons suppos que chaque formulaire tait soumis la mme URL avec une requte de type POST. Dans
certains cas, vous aurez besoin de changer ces paramtres. Vous pouvez le faire de diffrentes manires. Si
vous construisez votre formulaire dans un contrleur, vous pouvez utiliser setAction() et setMethod():
Listing 12-27

1 $form = $this->createFormBuilder($task)
2
->setAction($this->generateUrl('target_route'))
3
->setMethod('GET')
4
->add('task', 'text')
5
->add('dueDate', 'date')
6
->getForm();

Cet exemple suppose que vous avez cr une route appele target_route qui pointe vers le
contrleur qui va traiter le formulaire.

Dans Crer des Classes de Formulaire, vous apprendrez comment dporter le code qui construit votre
formulaire dans des classes spares. Lorsque vous utilisez une classe de formulaire externe dans votre
contrleur, vous pouvez passer les attributs action et method comme options de votre formulaire:
Listing 12-28

1 $form = $this->createForm(new TaskType(), $task, array(


2
'action' => $this->generateUrl('target_route'),
3
'method' => 'GET',
4 ));

Enfin, vous pouvez surcharger ces attributs dans un template en les passant aux fonctions form() ou
form_start() :
Listing 12-29

1 {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
2 {{ form(form, {'action': path('target_route'), 'method': 'GET'}) }}
3
4 {{ form_start(form, {'action': path('target_route'), 'method': 'GET'}) }}

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 161

Si la mthode n'est pas GET ou POST, mais PUT, PATCH ou DELETE, Symfony2 insrera
un nouveau champ cach avec le nom _method qui stockera cette mthode. Le formulaire sera
soumis dans une requte POST classique mais le routeur de Symfony2 sera capable de dtecter le
paramtre _method et interprtera la requte comme une requte PUT, PATCH ou DELETE. Lisez
le chapitre du Cookbook Comment utiliser des mthodes HTTP autres que GET et POST dans les
routes pour obtenir plus d'informations ce sujet.

Crer des Classes de Formulaire


Comme vous l'avez vu, un formulaire peut tre cr et utilis directement dans un contrleur. Cependant,
une meilleure pratique est de construire le formulaire dans une classe PHP spare et autonome, qui peut
ainsi tre rutilise n'importe o dans votre application. Crez une nouvelle classe qui va hberger la
logique de construction du formulaire task :
Listing 12-30

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('task');
$builder->add('dueDate', null, array('widget' => 'single_text'));
}
public function getName()
{
return 'task';
}
}

Cette nouvelle classe contient toutes les directives ncessaires la cration du formulaire task (notez
que la mthode getName() doit retourner un identifiant unique pour ce type de formulaire). Il peut
tre utilis pour construire rapidement un objet formulaire dans le contrleur:
Listing 12-31

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/TaskBundle/Controller/DefaultController.php
// ajoutez cette nouvelle dclaration use en haut de la classe
use Acme\TaskBundle\Form\Type\TaskType;
public function newAction()
{
$task = // ...
$form = $this->createForm(new TaskType(), $task);

// ...
}

Placer la logique du formulaire dans sa propre classe signifie que le formulaire peut tre rutilis
facilement ailleurs dans votre projet. C'est la meilleure manire de crer des formulaires, mais le choix
final vous revient.

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 162

Dfinir la data_class
Chaque formulaire a besoin de savoir le nom de la classe qui dtient les donnes sous-jacentes (par
exemple : Acme\TaskBundle\Entity\Task). Gnralement, cette information est devine grce
l'objet pass en second argument de la mthode createForm (c-a-d $task). Plus tard, quand
vous commencerez imbriquer des formulaires les uns avec les autres, cela ne sera plus suffisant.
Donc, bien que facultatif, c'est gnralement une bonne ide de spcifier explicitement l'option
data_class en ajoutant ce qui suit votre classe formulaire:
Listing 12-32

1
2
3
4
5
6
7
8

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Task',
));
}

Lorsque vous associez un formulaire un objet, tous les champs sont mapps. Chaque champ du
formulaire qui n'existe pas dans l'objet associ entrainera la leve d'une exception.
Dans le cas o vous avez besoin de champs supplmentaires dans le formulaire (par exemple une
checkbox Acceptez-vous les conditions d'utilisation ) qui ne doit pas tre mappe l'objet sousjacent, vous devez dfinir l'option mapped false:
Listing 12-33

1
2
3
4
5
6
7

use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('task');
$builder->add('dueDate', null, array('mapped' => false));
}

De plus, s'il y a des champs dans le formulaire qui ne sont pas inclus dans les donnes soumises,
ces champs seront dfinis null.
La donne du champ est accessible dans un contrleur de cette manire:
Listing 12-34

1 $form->get('dueDate')->getData();

Dfinir vos formulaire en tant que services


Dfinir votre formulaire en tant que service est une bonne pratique, et le rend trs facilement utilisable
dans votre application.
Listing 12-35

1 # src/Acme/TaskBundle/Resources/config/services.yml
2 services:
3
acme_demo.form.type.task:
4
class: Acme\TaskBundle\Form\Type\TaskType
5
tags:
6
- { name: form.type, alias: task }

Et c'est tout! Maintenant, vous pouvez utiliser votre type de formulaire directement dans un contrleur:

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 163

Listing 12-36

1
2
3
4
5
6
7
8
9
10

// src/Acme/TaskBundle/Controller/DefaultController.php
// ...
public function newAction()
{
$task = ...;
$form = $this->createForm('task', $task);

// ...
}

ou mme l'utiliser au sein d'un autre type de formulaire:


Listing 12-37

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/TaskBundle/Form/Type/ListType.php
// ...
class ListType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add('someTask', 'task');
}
}

Lisez Crer votre Type de Champ en tant que Service pour plus d'informations.

Formulaires et Doctrine
Le but d'un formulaire est de traduire les donnes d'un objet (par exemple : Task) en un formulaire
HTML et puis de transcrire en retour les donnes soumises par l'utilisateur l'objet original. En tant que
tel, le fait de persister l'objet Task dans la base de donnes n'a rien voir avec les formulaires. Mais, si
vous avez configur la classe Task de telle sorte qu'elle soit persiste via Doctrine (c-a-d que vous avez
ajout des mtadonnes de correspondance), alors sa persistance peut tre effectue aprs la soumission
d'un formulaire lorsque ce dernier est valide:
Listing 12-38

1 if ($form->isValid()) {
2
$em = $this->getDoctrine()->getManager();
3
$em->persist($task);
4
$em->flush();
5
6
return $this->redirect($this->generateUrl('task_success'));
7 }

Si, pour une quelconque raison, vous n'avez pas accs votre objet $task d'origine, vous pouvez le
rcuprer depuis le formulaire:
Listing 12-39

1 $task = $form->getData();

Pour plus d'informations, voir le chapitre sur l'ORM Doctrine.


Le plus important est de comprendre que lorsque le formulaire est li ( bind ), les donnes soumises
sont transfres l'objet sous-jacent immdiatement. Si vous souhaitez persister ces donnes, vous avez
simplement besoin de persister l'objet lui-mme (qui contient dj les donnes soumises).
PDF brought to you by
generated on January 14, 2015

Chapter 12: Formulaires | 164

Formulaires imbriqus
Souvent, vous allez vouloir construire un formulaire qui inclura des champs de beaucoup d'objets
diffrents. Par exemple, un formulaire de souscription pourrait contenir des donnes appartenant un
objet User ainsi qu' plusieurs objets Address. Heureusement, grer cela est facile et naturel avec le
composant formulaire.

Imbriquer un objet unique


Supposez que chaque Task appartienne un simple objet Category. Commencez, bien sr, par crer
l'objet Category:
Listing 12-40

1
2
3
4
5
6
7
8
9
10
11
12

// src/Acme/TaskBundle/Entity/Category.php
namespace Acme\TaskBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
class Category
{
/**
* @Assert\NotBlank()
*/
public $name;
}

Ensuite, ajoutez une nouvelle proprit category la classe Task:


Listing 12-41

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// ...
class Task
{
// ...

/**
* @Assert\Type(type="Acme\TaskBundle\Entity\Category")
*/
protected $category;
// ...
public function getCategory()
{
return $this->category;
}
public function setCategory(Category $category = null)
{
$this->category = $category;
}
}

Maintenant que votre application a t mise jour pour reflter nos nouvelles conditions requises, crez
une classe formulaire afin que l'objet Category puisse tre modifi par l'utilisateur:
Listing 12-42

1 // src/Acme/TaskBundle/Form/Type/CategoryType.php
2 namespace Acme\TaskBundle\Form\Type;

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 165

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Category',
));
}
public function getName()
{
return 'category';
}
}

Le but final est d'autoriser la Category d'une Task tre modifie directement dans le formulaire de la
tche. Pour accomplir cela, ajoutez un champ category l'objet TaskType dont le type est une instance
de la nouvelle classe CategoryType :
Listing 12-43

1
2
3
4
5
6
7
8

use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add('category', new CategoryType());
}

Les champs de CategoryType peuvent maintenant tre affichs ct de ceux de la classe TaskType. Pour
activer la validation sur CategoryType, ajoutez l'option cascade_validation TaskType:
Listing 12-44

1 public function setDefaultOptions(OptionsResolverInterface $resolver)


2 {
3
$resolver->setDefaults(array(
4
'data_class' => 'Acme\TaskBundle\Entity\Task',
5
'cascade_validation' => true,
6
));
7 }

Affichez les champs de Category de la mme manire que les champs de la classe Task :
Listing 12-45

1 {# ... #}
2
3 <h3>Category</h3>
4 <div class="category">
5
{{ form_row(form.category.name) }}

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 166

6 </div>
7
8 {# ... #}

Lorsque l'utilisateur soumet le formulaire, les donnes soumises pour les champs de Category sont
utilises pour construire une instance de Category qui est ensuite affecte au champ category de
l'instance de Task`.
L'instance Category est accessible naturellement via $task->getCategory() et peut tre persiste dans
la base de donnes ou utilise de toute autre manire que vous souhaitez.

Imbriquer une Collection de Formulaires


Vous pouvez aussi imbriquer une collection de formulaires dans un formulaire (imaginez un formulaire
Catgorie avec plusieurs sous-formulaires Produit). Cela peut tre accompli en utilisant le type de
champ collection.
Pour plus d'informations, lisez le chapitre du cookbook "Comment imbriquer une Collection de
Formulaires" et le chapitre sur le type de champ collection.

Habillage de Formulaire ( Theming )


Chaque partie de l'affichage d'un formulaire peut tre personnalise. Vous tes libre de changer la
manire dont chaque partie du formulaire est affiche, de changer les balises utilises pour afficher les
erreurs, ou mme de personnaliser la manire dont la balise textarea doit tre affiche. Tout est permis,
et diffrentes personnalisations peuvent tre utilises diffrents endroits.
Symfony utilise des templates pour afficher chaque partie d'un formulaire, comme les balises tags, les
balises input, les messages d'erreur et tout le reste.
Dans Twig, chaque fragment de formulaire est reprsent par un bloc Twig. Pour personnaliser
n'importe quelle partie d'un formulaire, vous avez juste besoin de rcrire le bloc appropri.
En PHP, chaque fragment de formulaire est affich via un fichier de template individuel. Pour
personnaliser n'importe quelle partie d'un formulaire, vous avez juste besoin de rcrire le template
existant en en crant un nouveau.
Pour comprendre comment tout cela fonctionne, personnalisons le fragment form_row et ajoutons
l'attribut class l'lment div qui entoure chaque ligne. Pour faire cela, crez un nouveau fichier de
template qui va stocker la nouvelle balise :
Listing 12-46

1
2
3
4
5
6
7
8
9
10

{# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #}
{% block form_row %}
{% spaceless %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}

Le fragment de formulaire form_row est utilis pour afficher la plupart des champs via la fonction
form_row. Pour dire au composant formulaire d'utiliser votre nouveau fragment form_row dfini cidessus, ajoutez ce qui suit en haut du template qui affiche le formulaire :
Listing 12-47

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 167

1
2
3
4
5
6

{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %}
{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig'
'AcmeTaskBundle:Form:fields2.html.twig' %}
{{ form(form) }}

La balise form_theme (dans Twig) importe les fragments dfinis dans le template donn et les utilise
lorsqu'il affiche le formulaire. En d'autres termes, quand la fonction form_row est appele plus tard dans
ce template, elle va utiliser le bloc form_row de votre thme personnalis ( la place du bloc par dfaut
form_row qui est fourni avec Symfony).
Votre thme personnalis n'a pas besoin de surcharger tous les blocs. Lorsqu'il affiche un bloc qui n'est
pas surcharg par votre thme personnalis, le moteur de thme se rabattra sur le thme global (dfini au
niveau du bundle).
Si plusieurs thmes personnaliss sont fournis, ils seront pris selon l'ordre dans lequel ils sont lists avant
que le thme global ne soit pris en compte.
Pour personnaliser n'importe quelle portion d'un formulaire, vous devez juste rcrire le fragment
appropri. Connatre exactement quel bloc ou fichier rcrire est le sujet de la prochaine section.
Listing 12-48

1 {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
2
3 {% form_theme form with 'AcmeTaskBundle:Form:fields.html.twig' %}
4
5 {% form_theme form with ['AcmeTaskBundle:Form:fields.html.twig',
'AcmeTaskBundle:Form:fields2.html.twig'] %}

Pour plus de prcisions, lisez Comment personnaliser le rendu de formulaire.

Nommage de Fragment de Formulaire


Dans Symfony, chaque partie d'un formulaire qui est affich - lments de formulaire HTML, erreurs,
labels, etc - est dfinie dans un thme de base, qui est une collection de blocs dans Twig et une collection
de fichiers de template dans PHP.
Dans Twig, chaque bloc ncessaire est dfini dans un unique fichier de template
(form_div_layout.html.twig12) qui se trouve dans le Twig Bridge13. Dans ce fichier, vous pouvez voir
chaque bloc requis pour afficher un formulaire et chaque type de champ par dfaut.
En PHP, les fragments sont des fichiers de template individuels. Par dfaut, ils sont situs dans le
rpertoire Resources/views/Form du bundle du framework (voir sur GitHub14).
Chaque nom de fragment suit le mme schma de base et est divis en deux parties, spares par un
unique underscore (_). Voici quelques exemples :
form_row - utilis par form_row pour afficher la plupart des champs ;
textarea_widget - utilis par form_widget pour afficher un champ de type textarea ;
form_errors - utilis par form_errors pour afficher les erreurs d'un champ.
Chaque fragment suit le mme schma de base : type_part. La partie type correspond au type du champ
qui doit tre affich (par exemple : textarea, checkbox, date, etc) alors que la partie part correspond

12. https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
13. https://github.com/symfony/symfony/tree/2.2/src/Symfony/Bridge/Twig
14. https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 168

ce qui va tre affich (par exemple : label, widget, errors, etc). Par dfaut, il y a 4 parties possibles d'un
formulaire qui peuvent tre affiches :
label

(par exemple : form_label)

affiche le label du champ

widget

(par exemple :
form_widget)

affiche la reprsentation HTML du champ

errors

(par exemple :
form_errors)

affiche les erreurs du champ

row

(par exemple : form_row)

affiche la ligne entire du champ (label, widget, et


erreurs)

Il y a en fait 2 autres parties, rows et rest, mais vous ne devriez que rarement, voire jamais, avoir
besoin de les rcrire.

En connaissant le type du champ (par exemple : textarea) et quelle partie de ce dernier vous souhaitez
personnaliser (par exemple : widget), vous pouvez construire le nom du fragment qui a besoin d'tre
rcrit (par exemple : textarea_widget).

Hritage de Fragment de Template


Dans certains cas, le fragment que vous voulez personnaliser sera absent. Par exemple, il n'y a pas de
fragment textarea_errors dans les thmes fournis par dfaut par Symfony. Alors comment les erreurs
des textareas sont-elles affiches ?
La rponse est : via le fragment form_errors. Quand Symfony affiche les erreurs d'un champ de type
textarea, il recherche en premier un fragment textarea_errors avant de se replier sur le fragment de
secours form_errors. Chaque type de champ a un type parent (le type parent de textarea est field), et
Symfony l'utilise si le fragment de base n'existe pas.
Donc, afin de rcrire les erreurs pour les champs textarea seulement, copiez le fragment form_errors,
renommez-le en textarea_errors et personnalisez-le. Pour rcrire le rendu d'erreur par dfaut pour
tous les champs, copiez et personnalisez le fragment form_errors directement.
Le type parent de chaque type de champ est disponible dans la rfrence de type de formulaire
pour chaque type de champ.

Habillage global de Formulaire


Dans l'exemple ci-dessus, vous avez utilis la fonction d'aide form_theme (dans Twig) pour importer
les fragments personnaliss de formulaire l'intrieur de ce formulaire uniquement. Vous pouvez aussi
dire Symfony d'importer les personnalisations de formulaire travers votre projet entier.

Twig
Pour inclure automatiquement les blocs personnaliss du template fields.html.twig crs plus tt dans
tous les templates, modifiez votre fichier de configuration d'application :
Listing 12-49

1 # app/config/config.yml
2 twig:

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 169

3
4
5
6

form:
resources:
- 'AcmeTaskBundle:Form:fields.html.twig'
# ...

Tous les blocs se trouvant dans le template fields.html.twig sont maintenant utiliss pour l'affichage
de tous vos formulaires.

Personnaliser l'affichage de formulaire dans un fichier unique avec Twig


Dans Twig, vous pouvez aussi personnaliser un bloc de formulaire directement l'intrieur du
template ncessitant une personnalisation :
Listing 12-50

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

{% extends '::base.html.twig' %}

{# importe _self en tant qu'habillage du formulaire #}


{% form_theme form _self %}
{# effectue la personnalisation du fragment de formulaire #}
{% block form_row %}
{# personnalisez l'affichage de la ligne du champ #}
{% endblock form_row %}
{% block content %}
{# ... #}
{{ form_row(form.task) }}
{% endblock %}

La balise {% form_theme form _self %} permet aux blocs de formulaire d'tre personnaliss
directement l'intrieur du template qui va utiliser ces personnalisations. Utilisez cette mthode
pour faire des personnalisations de formulaire qui ne seront ncessaires que dans un unique
template.
Cette fonctionnalit ({% form_theme form _self %}) ne marchera que si votre template en
tend un autre. Si ce n'est pas le cas, vous devrez faire pointer form_theme vers un template
spar.

PHP
Pour inclure automatiquement les templates personnaliss du rpertoire Acme/TaskBundle/Resources/
views/Form crs plus tt dans tous les templates, modifiez votre fichier de configuration d'application :
Listing 12-51

1 # app/config/config.yml
2 framework:
3
templating:
4
form:
5
resources:
6
- 'AcmeTaskBundle:Form'
7 # ...

Tous les fragments l'intrieur du rpertoire Acme/TaskBundle/Resources/views/Form sont


maintenant utiliss pour l'affichage de tous vos formulaires.

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 170

Protection CSRF
CSRF - ou Cross-site request forgery15 - est une mthode grce laquelle un utilisateur malveillant tente
de faire soumettre inconsciemment des donnes un utilisateur lgitime qui n'avait pas l'intention de
les soumettre. Heureusement, les attaques CSRF peuvent tre contres en utilisant un jeton CSRF
l'intrieur de vos formulaires.
La bonne nouvelle est que, par dfaut, Symfony prend en charge et valide automatiquement les jetons
CSRF pour vous. Cela signifie que vous pouvez profiter de la protection CSRF sans n'avoir rien faire.
En fait, chaque formulaire dans ce chapitre a profit de la protection CSRF !
La protection CSRF fonctionne en ajoutant un champ cach dans votre formulaire - appel _token
par dfaut - qui contient une valeur que seuls vous et votre utilisateur connaissez. Cela garantit que
l'utilisateur - et lui seulement - soumet les informations donnes. Symfony valide automatiquement la
prsence et l'exactitude de ce jeton.
Le champ _token est un champ cach et sera affich automatiquement si vous incluez la fonction
form_end() dans votre template, qui garantit que tous les champs non affichs sont dlivrs en sortie.
Le jeton CSRF peut tre personnalis pour chacun des formulaires. Par exemple:
Listing 12-52

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
// ...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class'
=> 'Acme\TaskBundle\Entity\Task',
'csrf_protection' => true,
'csrf_field_name' => '_token',
// une cl unique pour aider la gnration du jeton secret
'intention'
=> 'task_item',
));
}

// ...
}

Pour dsactiver la protection CSRF, dfinissez l'option csrf_protection false. Les personnalisations
peuvent aussi tre effectues globalement dans votre projet. Pour plus d'informations, voir la section de
rfrence de configuration de formulaire.
L'option intention est optionnelle, mais amliore grandement la scurit du jeton gnr en le
rendant diffrent pour chaque formulaire.

Utiliser un formulaire sans classe


Dans la plupart des cas, un formulaire est associ un objet, et les champs du formulaire affichent et
stockent leurs donnes dans les proprits d'un objet. C'est exactement ce que vous avez vu jusqu'ici dans
ce chapitre avec la classe Task.
15. http://fr.wikipedia.org/wiki/Cross-site_request_forgery

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 171

Mais parfois, vous voudrez juste utiliser un formulaire sans une classe, et obtenir un tableau des donnes
soumises. C'est en fait trs facile:
Listing 12-53

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// Assurez vous d'importer le namespace Request namespace en haut de la classe


use Symfony\Component\HttpFoundation\Request;
// ...
public function contactAction(Request $request)
{
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
->add('name', 'text')
->add('email', 'email')
->add('message', 'textarea')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// Les donnes sont un tableau avec les cls "name", "email", et "message"
$data = $form->getData();
}

// ... affiche le formulaire


}

Par dfaut, en fait, un formulaire part du principe que vous voulez travailler avec un tableau de donnes
plutt qu'avec un objet. Il y a exactement deux faons de changer ce comportement et d'associer le
formulaire avec un objet la place :
1. Passez un objet lors de la cration du formulaire (comme premier argument de
createFormBuilder ou deuxime argument de createForm);
2. Dfinissez l'option data_class de votre formulaire.
Si vous ne faites pas l'un ou l'autre, alors le formulaire retournera les donnes dans un tableau. Dans cet
exemple, puisque $defaultData n'est pas un objet (et que l'option data_class n'est pas dfinie), $form>getData() retournera finalement un tableau.
Vous pouvez galement accder directement aux valeurs POST (dans ce cas "name") par le biais de
l'objet Request, comme ceci:
Listing 12-54

1 $this->get('request')->request->get('name');

Notez cependant que, dans la plupart des cas, utiliser la mthode getData() est le meilleur choix,
puisqu'elle retourne la donne (souvent un objet) aprs qu'elle soit transforme par le framework.

Ajouter la Validation
La seule pice manquante est la validation. D'habitude, quand vous appelez $form->isValid(), l'objet
est valid en parcourant les contraintes que vous appliquez sa classe. Si le formulaire est associ un
objet (c-a-d si vous utilisez l'option data_class ou si vous passez un object votre formulaire), c'est
l'approche que vous voudrez utiliser dans la plupart des cas. lisez Validation pour plus de dtails.
Mais si le formulaire n'est pas associ un objet et que vous voulez plutt utiliser un simple tableau pour
soumettre vos donnes, comment ajouter des contraintes aux donnes de votre formulaire ?
La rponse est de dfinir les contraintes vous-mme, et de les attacher chacun des champs. L'approche
globale est un peu plus explique dans le chapitre validation, mais voici un petit exemple:

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 172

Listing 12-55

1
2
3
4
5
6
7
8
9
10
11
12
13
14

use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
$builder
->add('firstName', 'text', array(
'constraints' => new Length(array('min' => 3)),
))
->add('lastName', 'text', array(
'constraints' => array(
new NotBlank(),
new Length(array('min' => 3)),
),
))
;

Si vous utilisez les groupes de validation, vous devrez soit rfrencer un groupe Default lorsque
vous crez le formulaire, ou soit dfinir le bon groupe la contrainte que vous ajoutez.

Listing 12-56

1 new NotBlank(array('groups' => array('create', 'update'))

Rflexions finales
Vous connaissez maintenant tout sur les bases ncessaires la construction de formulaires complexes et
fonctionnels pour votre application. Quand vous construisez des formulaires, gardez l'esprit que le but
premier d'un formulaire est de transcrire les donnes d'un objet (Task) en un formulaire HTML afin que
l'utilisateur puisse modifier ces donnes. Le second objectif d'un formulaire est de prendre les donnes
soumises par l'utilisateur et de les rappliquer l'objet.
Il y a beaucoup plus apprendre propos du puissant monde des formulaires, par exemple comment
grer les uploads de fichier avec Doctrine ou comment crer un formulaire o un nombre dynamique de
sous-formulaires peut tre ajout (par exemple : une liste de choses faire o vous pouvez continuer
d'ajouter des champs via Javascript avant de soumettre le formulaire). Voyez le cookbook pour ces sujets.
Aussi, assurez-vous de vous appuyer sur la documentation de rfrence des types de champ, qui inclut des
exemples de comment utiliser chaque type de champ et leurs options.

En savoir plus grce au Cookbook

Comment grer les uploads de fichier avec Doctrine


Rfrence du Champ Fichier
Crer des Types de Champ Personnaliss
Comment personnaliser le rendu de formulaire
Comment modifier dynamiquement les formulaires en utilisant les vnements
Comment utiliser les Convertisseurs de Donnes

PDF brought to you by


generated on January 14, 2015

Chapter 12: Formulaires | 173

Chapter 13

La scurit
La scurit est un processus comprenant 2 tapes, dont le but est de prvenir un utilisateur d'accder
une ressource laquelle il n'a pas accs.
Dans la premire tape du processus, le systme de scurit identifie l'utilisateur en lui demandant de
soumettre une sorte d'identification. C'est ce qu'on appelle l'authentification, et cela signifie que le
systme cherche savoir qui vous tes.
Une fois que le systme sait qui vous tes, l'tape suivante est de dterminer si vous avez accs une
ressource donne. Cette tape du processus est appele autorisation, et cela signifie que le systme
vrifie si vous avez les privilges pour excuter certaines actions.

Comme la meilleure faon d'apprendre est par l'exemple, alors plongeons dans le vif du sujet.
Le composant de scurit de Symfony est disponible en tant que bibliothque indpendante, et peut
tre utilis pour tout projet PHP.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 174

Exemple simple: l'authentification HTTP


Le composant de scurit peut tre configur grce aux fichiers de configurations de l'application. En
fait, la plupart des rglages de scurit ne ncessitent que l'utilisation d'une configuration adquate. La
configuration suivante indique Symfony de scuriser toute URL correspondant au format /admin/* et
de demander l'utilisateur de s'authentifier en utilisant l'authentification HTTP (c'est--dire un bon vieux
systme avec login/mot de passe) :
Listing 13-1

1 # app/config/security.yml
2 security:
3
firewalls:
4
secured_area:
5
pattern:
^/
6
anonymous: ~
7
http_basic:
8
realm: "Secured Demo Area"
9
10
access_control:
11
- { path: ^/admin, roles: ROLE_ADMIN }
12
13
providers:
14
in_memory:
15
memory:
16
users:
17
ryan: { password: ryanpass, roles: 'ROLE_USER' }
18
admin: { password: kitten, roles: 'ROLE_ADMIN' }
19
20
encoders:
21
Symfony\Component\Security\Core\User\User: plaintext

La distribution Symfony Standard place la configuration de la scurit dans un fichier spar (app/
config/security.yml). Si vous ne voulez pas utiliser un fichier spar, vous pouvez mettre la
configuration directement dans le fichier principal de configuration (app/config/config.yml).

Le rsultat final de cette configuration est un systme de scurit entirement fonctionnel, que l'on peut
dcrire de la manire suivante :
Il y a 2 utilisateurs dans le systme (ryan et admin);
Les utilisateurs s'authentifient grce une authentification basique HTTP;
Toute URL correspondant au format /admin/* est scurise, et seul l'utilisateur admin peut y
accder
Toutes les URLs qui ne correspondent pas au format /admin/* sont accessibles par tous les
utilisateurs (et l'utilisateur n'aura pas s'authentifier).
Voyons rapidement comment la scurit fonctionne et quel est le rle de chaque lment de la
configuration.

Comment fonctionne la scurit : authentification et autorisation


Le systme de scurit de Symfony commence par dterminer qui est l'utilisateur (c'est l'authentification)
puis il voit si l'utilisateur a accs une ressource ou une URL.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 175

Pare-feu (authentification)
Lorsqu'un utilisateur fait une requte une URL qui est protge par un pare-feu (firewall), le systme
de scurit est activ. Le rle du pare-feu est de dterminer si un utilisateur doit ou ne doit pas
tre authentifi, et s'il doit l'tre, de retourner une rponse l'utilisateur afin d'entamer le processus
d'authentification.
Un pare-feu est activ lorsque l'URL d'une requte correspond un masque d'expression rgulire
contenu dans la configuration du pare-feu. Dans cet exemple, le masque (^/) va correspondre toutes
les requtes entrantes. Le fait que le pare-feu soit activ ne veut pas dire que la boite d'authentification
HTTP contenant les champs nom d'utilisateur et mot de passe sera affiche pour chaque requte.
Par exemple, tout utilisateur peut accder /foo sans qu'on lui demande de s'authentifier.

Cela fonctionne d'abord parce que le pare-feu autorise les utilisateurs anonymes grce au paramtre
de configuration anonymous. En d'autres termes, un pare-feu ne ncessite pas qu'un utilisateur soit
totalement authentifi immdiatement. Et comme aucun role n'est ncessaire pour accder l'URL /foo
(dans la section access_control), la requte peut tre satisfaite sans jamais demander l'utilisateur de
s'authentifier.
Si vous supprimez la cl anonymous, le pare-feu va toujours demander l'utilisateur de s'authentifier
immdiatement.

Contrle d'accs (autorisation)


Par contre, si un utilisateur demande /admin/foo, le systme se comporte diffremment. C'est cause de
la section de la configuration access_control qui stipule que toute requte correspondant au masque
d'expression rgulire ^/admin (c'est--dire /admin ou tout ce qui correspond /admin/*) requiert le rle
ROLE_ADMIN. Les rles sont la base de la plupart des mcanismes d'autorisation : un utilisateur peut
accder /admin/foo seulement s'il possde le role ROLE_ADMIN.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 176

Comme prcdemment, quand l'utilisateur fait une requte, le pare-feu ne lui demande pas de
s'authentifier. Par contre, ds que la couche de contrle d'accs refuse l'accs l'utilisateur (parce que
l'utilisateur anonyme ne possde pas le rle ROLE_ADMIN), le pare-feu entre en action et initialise le
processus d'authentification. Le processus d'authentification dpend du mcanisme d'authentification
que vous utilisez. Par exemple, si vous utilisez la mthode d'authentification par formulaire de connexion,
l'utilisateur sera redirig la page de formulaire de connexion. Si vous utilisez l'authentification HTTP,
l'utilisateur recevra une rponse HTTP 401 et verra donc la boite contenant les champs login et mot de
passe.
L'utilisateur a maintenant la possibilit de soumettre ses informations d'identification l'application. Si
ces informations sont valides, la requte initiale peut tre lance nouveau.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 177

Dans cet exemple, l'utilisateur ryan s'authentifie avec succs auprs du pare-feu. Mais comme ryan n'a
pas le rle ROLE_ADMIN, il se verra refuser l'accs /admin/foo. Enfin, cela veut dire que l'utilisateur verra
un message indiquant que l'accs lui est refus.
Quand Symfony refuse l'utilisateur l'accs, l'utilisateur voit une page d'erreur et recevra un code
d'erreur HTTP 403 (Forbidden). Vous pouvez personnaliser la page d'erreur pour refus d'accs
en suivant les instructions se trouvant dans la page du cookbook Comment personnaliser les pages
d'erreur pour personnaliser la page d'erreur 403.

Enfin, si l'utilisateur admin demande /admin/foo, un processus similaire se droule, sauf que maintenant,
aprs s'tre authentifi, la couche de contrle d'accs va laisser la requte s'excuter :

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 178

Les tapes excutes lorsqu'un utilisateur demande une ressource protge sont simples, mais
extrmement flexibles. Comme vous le verrez plus tard, l'authentification peut tre prise en charge de
multiples faons, incluant les formulaires de connexion, les certificats X.509, ou les authentifications via
Twitter. Quel que soit la mthode d'authentification, les tapes sont toujours les mmes :
1. Un utilisateur accde une ressource protge;
2. L'application redirige l'utilisateur au formulaire de connexion;
3. L'utilisateur soumet ses informations d'identification (par exemple login/mot de passe);
4. Le pare-feu authentifie l'utilisateur;
5. L'utilisateur authentifi renvoie la requte initiale.
Le processus exact dpend en fait lgrement du mcanisme d'authentification que vous utilisez.
Par exemple, lorsque le formulaire de connexion est utilis, l'utilisateur soumet ses informations
d'identification une URL qui traite le formulaire (par exemple /login_check) et est ensuite
redirig l'URL qu'il a demande initialement (par exemple /admin/foo). Par contre, avec
l'authentification HTTP, l'utilisateur soumet ses informations d'identification directement l'URL
initiale (par exemple /admin/foo) et la page est retourne dans la mme requte (donc pas de
redirection).
Ces comportements diffrents (types d'idiosyncrasie) ne devraient pas vous causer de problmes,
mais il est bon de les garder l'esprit.

Vous apprendrez plus tard comment tout peut tre scuris avec Symfony2, incluant certains
contrleurs, objets, ou mme mthodes PHP.

Utilisation d'un formulaire de connexion traditionnel

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 179

Dans cette section, vous allez apprendre comment crer un formulaire de connexion basique qui
continue d'utiliser les utilisateurs cods en dur que vous avez dfini dans le fichier security.yml.
Pour charger les utilisateurs de la base de donnes, lisez Comment charger les utilisateurs depuis la
base de donnes (le fournisseur d'Entit). En lisant cet article et cette section, vous pouvez crer un
systme de connexion complet qui charge les utilisateurs dans la base de donnes.

Pour l'instant, vous avez vu comment protger votre application derrire un pare-feu et ensuite comment
protger l'accs certaines zones en utilisant les rles. En utilisant l'authentification HTTP, vous pouvez
sans effort profiter de la boite login/mot de passe offert par tous les navigateurs. Mais Symfony comprend
plusieurs mcanismes d'authentification par dfaut. Pour plus de dtails sur chacun d'eux, rfrez-vous
la documentation de rfrence sur la configuration de la scurit.
Dans cette section, vous allez amliorer le processus en autorisant l'utilisateur s'authentifier via un
formulaire de connexion traditionnel.
D'abord, activez le formulaire de connexion ( form login ) de votre pare-feu:
Listing 13-2

1 # app/config/security.yml
2 security:
3
firewalls:
4
secured_area:
5
pattern:
^/
6
anonymous: ~
7
form_login:
8
login_path:
9
check_path:

/login
/login_check

Si vous ne voulez pas personnaliser les valeurs de login_path ou check_path (les valeurs utilises
ici sont celles par dfaut), vous pouvez raccourcir votre configuration :
Listing 13-3

1 form_login: ~

Maintenant, quand le systme de scurit initie le processus d'authentification, il va rediriger l'utilisateur


au formulaire de connexion (/login by default). L'implmentation de ce formulaire de connexion est
de toute vidence votre responsabilit. Tout d'abord, crez 2 routes : une qui affiche le formulaire de
connexion (ici, /login) et une qui va prendre en charge la soumission du formulaire (ici, /login_check)
:
Listing 13-4

1 # app/config/routing.yml
2 login:
3
pattern:
/login
4
defaults: { _controller: AcmeSecurityBundle:Security:login }
5 login_check:
6
pattern:
/login_check

Vous n'avez pas implmenter un contrleur pour l'URL /login_check car le pare-feu va
automatiquement intercepter et traiter tout formulaire soumis cette URL.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 180

New in version 2.1: Dans Symfony 2.1, vous devez avoir des routes configures pour vos URLs
login_path (ex /login), check_path (ex /login_check) et logout (ex /logout - voir Se
dconnecter).

Veuillez noter que le nom de la route login n'est pas important. Ce qui importe est que l'URL de la route
(login) corresponde la valeur de login_path, car c'est l que le systme de scurit va rediriger les
utilisateurs qui doivent se connecter.
Ensuite, crez un contrleur qui va afficher le formulaire de connexion:
Listing 13-5

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

// src/Acme/SecurityBundle/Controller/SecurityController.php;
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render('AcmeSecurityBundle:Security:login.html.twig', array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error'
=> $error,
));
}
}

Ne vous laissez pas impressionner par le contrleur. Comme vous allez le voir dans un moment, lorsque
l'utilisateur soumet le formulaire, le systme de scurit prend en charge automatiquement le formulaire
soumis. Si l'utilisateur venait soumettre un login ou un mot de passe invalide, ce formulaire lit les
erreurs de soumission du systme de scurit afin qu'elles soient ensuite affiches l'utilisateur.
En d'autres termes, votre rle est d'afficher le formulaire de connexion et toute erreur qui aurait pu
survenir, mais c'est le systme de scurit lui-mme qui prend en charge la validation du login et du mot
de passe et qui authentifie l'utilisateur.
Il ne nous reste qu' crer le template correspondant :
Listing 13-6

1
2
3
4
5
6
7
8
9
10

{# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
<label for="username">Login :</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Mot de passe :</label>

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 181

11
<input type="password" id="password" name="_password" />
12
13
{#
14
Si vous voulez contrler l'URL vers laquelle l'utilisateur est redirig en cas de
15 succs
16
(plus de dtails ci-dessous)
17
<input type="hidden" name="_target_path" value="/account" />
18
#}
19
20
<button type="submit">login</button>
</form>

La variable error passe au template est une instance de AuthenticationException1. Elle peut
contenir plus d'informations - et mme des informations sensibles - propos de l'chec de
l'authentification, alors utilisez l judicieusement !

Le formulaire a trs peu d'exigence. D'abord, en soumettant le formulaire /login_check (via la route
login_check), le systme de scurit va intercepter la soumission du formulaire et traiter le formulaire
automatiquement. Ensuite, le systme de scurit s'attend ce que les champs soumis soient nomms
_username et _password (le nom de ces champs peut tre configur).
Et c'est tout ! Lorsque vous soumettez le formulaire, le systme de scurit va automatiquement vrifier
son identit et va soit authentifier l'utilisateur, soit renvoyer l'utilisateur au formulaire de connexion, o
les erreurs vont tre affiches.
Rcapitulons tout le processus :
1. L'utilisateur cherche accder une ressource qui est protge;
2. Le pare-feu initie le processus d'authentification en redirigeant l'utilisateur au formulaire de
connexion (/login);
3. La page /login affiche le formulaire de connexion en utilisant la route et le formulaire crs
dans cet exemple.
4. L'utilisateur soumet le formulaire de connexion /login_check;
5. Le systme de scurit intercepte la requte, vrifie les informations d'identification soumises
par l'utilisateur, authentifie l'utilisateur si elles sont correctes et renvoie l'utilisateur au
formulaire de connexion si elles ne le sont pas.
Par dfaut, si les informations d'identification sont correctes, l'utilisateur va tre redirig la page
originale qu'il avait demande (par exemple /admin/foo). Si l'utilisateur est all directement au
formulaire de connexion, il sera redirig la page d'accueil. Cela peut tre entirement configur, en vous
permettant, par exemple, de rediriger l'utilisateur vers une URL spcifique.
Pour plus de dtails, et savoir comment personnaliser le processus de connexion par formulaire en
gnral, veuillez vous reporter Comment personnaliser votre formulaire de login.

1. http://api.symfony.com/2.6/Symfony/Component/Security/Core/Exception/AuthenticationException.html

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 182

viter les erreurs courantes


Lorsque vous configurez le formulaire de connexion, faites attention aux piges.
1. Crez les routes adquates
D'abord, assurez-vous que vous avez dfini les routes /login et /login_check correctement et
qu'elles correspondent aux valeurs de configuration login_path et check_path. Une mauvaise
configuration ici pourrait vouloir dire que vous seriez redirig une page 404 au lieu de la page de
connexion, ou que la soumission du formulaire ne fasse rien (vous ne verriez que le formulaire de
connexion encore et encore).
2. Assurez-vous que la page de connexion n'est pas scurise
Aussi, assurez-vous que la page de connexion ne requiert pas un rle particulier afin d'tre affiche.
Par exemple, la configuration suivante - qui ncessite le rle ROLE_ADMIN pour toutes les URLs
(incluant l'URL /login), va provoquer une boucle de redirection :
Listing 13-7

1 access_control:
2
3
- { path: ^/, roles: ROLE_ADMIN }

Il suffit d'enlever le contrle d'accs pour l'URL /login URL pour corriger le problme :
Listing 13-8

1 access_control:
2
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
3
- { path: ^/, roles: ROLE_ADMIN }

Aussi, si votre pare-feu n'autorise pas les utilisateurs anonymes, vous devrez crer un pare-feu
spcial qui permet l'accs l'utilisateur anonyme d'accder la page de connexion :
Listing 13-9

1 firewalls:
2
login_firewall:
3
pattern:
4
anonymous:
5
secured_area:
6
pattern:
7
form_login:

^/login$
~
^/
~

3. Assurez-vous que /login_check est derrire un pare-feu


Ensuite, assurez-vous que l'URL check_path (ici, /login_check) est derrire le pare-feu que vous
utilisez pour le formulaire de connexion (dans cet exemple, le pare-feu unique correspond toutes
les URLs, incluant /login_check). Si /login_check n'est pris en charge par aucun pare-feu, vous
obtiendrez une exception Unable to find the controller for path "/login_check".
4. Plusieurs pare-feu ne partagent pas de contexte de scurit
Si vous utilisez plusieurs pare-feu et que vous vous authentifiez auprs d'un pare-feu, vous ne serez
pas automatiquement authentifi auprs des autres pare-feu automatiquement. Diffrents pare-feu
sont comme plusieurs systmes de scurit. C'est pourquoi, pour la plupart des applications, avoir
un seul pare-feu est suffisant.

Autorisation
La premire tape en scurit est toujours l'authentification : le processus de vrifier l'identit de
l'utilisateur. Avec Symfony, l'authentification peut tre faite de toutes les faons voulues - au travers d'un
formulaire de connexion, de l'authentification HTTP, ou mme de facebook.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 183

Une fois l'utilisateur authentifi, l'autorisation commence. L'autorisation fournit une faon standard et
puissante de dcider si un utilisateur peut accder une ressource (une URL, un objet du modle, un appel
de mthode...). Cela fonctionne en assignant des rles chaque utilisateur, et d'ensuite en requrant
diffrents rles pour diffrentes ressources.
Le processus d'autorisation comporte 2 aspects :
1. Un utilisateur possde un ensemble de rles;
2. Une ressource requiert un rle spcifique pour tre atteinte.
Dans cette section, vous verrez en dtail comment scuriser diffrentes ressources (ex. URLs, appels de
mthodes...) grce aux rles. Plus tard, vous apprendrez comment les rles peuvent tre crs et assigns
aux utilisateurs.

Scurisation d'URLs spcifiques


La faon la plus simple pour scuriser une partie de votre application est de scuriser un masque d'URL
au complet. Vous avez dj vu dans le premier exemple de ce chapitre, o tout ce qui correspondait
l'expression rgulire ^/admin ncessite le role ROLE_ADMIN.
Vous pouvez dfinir autant de masque d'URL que vous voulez - chacune tant une expression rgulire.
Listing 13-10

1 # app/config/security.yml
2 security:
3
# ...
4
access_control:
5
- { path: ^/admin/users, roles: ROLE_SUPER_ADMIN }
6
- { path: ^/admin, roles: ROLE_ADMIN }

En prfixant votre chemin par ^, vous vous assurez que seules les URLs commenant par le masque
correspondent. Par exemple, un chemin spcifiant simplement /admin (sans le ^) reconnatra une
url du type /admin/foo mais aussi /foo/admin.

Pour chaque requte entrante, Symfony essaie de trouver une rgle d'accs de contrle (la premire
gagne). Si l'utilisateur n'est pas encore authentifi, le processus d'authentification est initi (c'est--dire
que l'utilisateur a une chance de se connecter). Mais si l'utilisateur est authentifi, mais qu'il ne possde
pas le rle ncessaire, une exception AccessDeniedException2 est lance, qui peut tre attrape et
convertie en une belle page d'erreur accs refus prsente l'utilisateur. Voir Comment personnaliser
les pages d'erreur pour plus d'informations.
Comme Symfony utilise la premire rgle d'accs de contrle qui correspond, une URL comme /admin/
users/new correspondra la premire rgle et ne ncessitera que le rle ROLE_SUPER_ADMIN. Tout URL
comme /admin/blog correspondra la seconde rgle et ncessitera donc ROLE_ADMIN.

Scuriser par IP
Dans certaines situations qui peuvent survenir, vous aurez besoin de restreindre l'accs une route
donne base sur une IP. C'est particulirement le cas des Edge Side Includes (ESI), par exemple, qui
utilisent une route nomme _internal . Lorsque les ESI sont utiliss, la route _internal est requise
par la passerelle de cache pour activer diffrentes options de cache pour les portions d'une mme page.
Dans la Standard Edition, cette route est prfixe par dfaut par ^/_internal (en supposant que vous avez
dcomment ces lignes dans le fichier de routage)
Ci-dessous un exemple de comment scuriser une route d'un accs externe :
Listing 13-11

2. http://api.symfony.com/2.6/Symfony/Component/Security/Core/Exception/AccessDeniedException.html

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 184

1 # app/config/security.yml
2 security:
3
# ...
4
access_control:
5
- { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }

Scuriser par canal


Tout comme la scurisation base sur IP, obliger l'usage d'SSL est aussi simple qu'ajouter une nouvelle
entre access_control :
Listing 13-12

1 # app/config/security.yml
2 security:
3
# ...
4
access_control:
5
- { path: ^/_internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https
}

Scuriser un contrleur
Protger votre application en utilisant des masques d'URL est facile, mais pourrait ne pas offrir une
granularit suffisante dans certains cas. Si ncessaire, vous pouvez facilement forcer l'autorisation dans
un contrleur:
Listing 13-13

1
2
3
4
5
6
7
8
9

use Symfony\Component\Security\Core\Exception\AccessDeniedException;
// ...
public function helloAction($name)
{
if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
// ...
}

Vous pouvez aussi choisir d'installer et d'utiliser le Bundle JMSSecurityExtraBundle, qui peut scuriser
un contrleur en utilisant les annotations:
Listing 13-14

1
2
3
4
5
6
7
8

use JMS\SecurityExtraBundle\Annotation\Secure;
/**
* @Secure(roles="ROLE_ADMIN")
*/
public function helloAction($name)
{
// ...
}

Pour plus d'informations, voir la documentation de JMSSecurityExtraBundle3. Si vous utilisez la


distribution standard de Symfony, ce bundle est disponible par dfaut. Sinon, vous pouvez facilement le
tlcharger et l'installer.

3. http://jmsyst.com/bundles/JMSSecurityExtraBundle/1.2

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 185

Scuriser d'autres services


En fait, tout dans Symfony peut tre protg en utilisant une stratgie semblable celle dcrite dans
les sections prcdentes. Par exemple, supposez que vous avez un service (une classe PHP par exemple)
dont la responsabilit est d'envoyer des courriels d'un utilisateur un autre. Vous pouvez restreindre
l'utilisation de cette classe - peu importe d'o vous l'utilisez - des utilisateurs qui ont des rles
spcifiques.
Pour plus d'informations sur la manire d'utiliser le composant de scurit pour scuriser diffrents
services et mthodes de votre application, voir Comment scuriser n'importe quel service ou mthode de
votre application.

Listes de contrle d'accs (ACL): scuriser des objets de la base de donnes


Imaginez que vous tes en train de concevoir un systme de blog o les utilisateurs peuvent crire des
commentaires sur les articles. Mais vous voulez qu'un utilisateur puisse diter ses propres commentaires,
mais pas les autres utilisateurs. Aussi, vous, en tant qu'administrateur, voulez pouvoir diter tous les
commentaires.
Le composant de scurit comprend un systme de liste de contrle d'accs (Access Control List, ou ACL)
que vous pouvez utiliser pour contrler l'accs des instances individuelles de votre systme. Sans la liste
d'accs de contrle, vous pouvez scuriser votre systme pour que seulement certains utilisateurs puissent
diter les commentaires en gnral. Mais avec la liste d'accs de contrle, vous pouvez restreindre ou
autoriser l'accs un commentaire en particulier.
Pour plus d'informations, reportez-vous l'article du cookbook Comment utiliser les Access Control Lists
(ACLs) ( liste de contrle d'accs en franais).

Les utilisateurs
Dans les sections prcdentes, vous avez appris comment vous pouvez protger diffrentes ressources en
exigeant un ensemble de rles pour une ressource. Cette section aborde l'autre aspect de l'autorisation :
les utilisateurs.

D'o viennent les utilisateurs (Fournisseurs d'utilisateurs)


Au cours de l'authentification, l'utilisateur soumet ses informations d'identit (gnralement un login et
un mot de passe). La responsabilit du systme d'authentification est de faire correspondre cette identit
avec un ensemble d'utilisateurs. Mais d'o cet ensemble provient-il?
Dans Symfony2, les utilisateurs peuvent provenir de n'importe o - un fichier de configuration, une table
de base de donnes, un service Web, ou tout ce que vous pouvez imaginer d'autre. Tout ce qui fournit
un ou plusieurs utilisateurs au systme d'authentification est appel fournisseur d'utilisateurs (User
Provider). Symfony2 comprend en standard deux des fournisseurs les plus utiliss : un qui charge ses
utilisateurs depuis un fichier de configuration, et un autre qui charge ses utilisateurs d'une table de base
de donnes.

Spcifier les utilisateurs dans un fichier de configuration


La manire la plus simple de dfinir des utilisateurs est de la faire directement dans un fichier de
configuration. En fait, vous avez dj vu cet exemple dans ce chapitre.
Listing 13-15

1 # app/config/security.yml
2 security:
3
# ...
4
providers:

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 186

5
6
7
8

default_provider:
users:
ryan: { password: ryanpass, roles: 'ROLE_USER' }
admin: { password: kitten, roles: 'ROLE_ADMIN' }

Ce fournisseur d'utilisateurs est appel fournisseur d'utilisateurs en mmoire ( in-memory ) car les
utilisateurs ne sont pas sauvegards dans une base de donnes. L'objet User est fourni par Symfony
(User4).
Tout fournisseur d'utilisateur peut charger des utilisateurs directement de la configuration en
spcifiant le paramtre de configuration users et en listant les utilisateurs en dessous.

Si votre login est compltement numrique (par exemple 77) ou contient un tiret (par exemple
user-name), vous devez utiliser une syntaxe alternative pour dfinir les utilisateurs en YAML:
Listing 13-16

1 users:
2
- { name: 77, password: pass, roles: 'ROLE_USER' }
3
- { name: user-name, password: pass, roles: 'ROLE_USER' }

Pour les petits sites, cette mthode est rapide et facile mettre en place. Pour des systmes plus
complexes, vous allez vouloir charger vos utilisateurs de la base de donnes.

Charger les utilisateurs de la base de donnes


Si vous voulez charger vos utilisateurs depuis l'ORM Doctrine, vous pouvez facilement le faire en crant
une classe User et en configurant le fournisseur d'entits (entity provider).
Un bundle de trs grande qualit est disponible, qui permet de sauvegarder vos utilisateurs depuis
l'ORM ou l'ODM de Doctrine. Apprenez-en plus sur le FOSUserBundle5 sur GitHub.

Avec cette approche, vous devez d'abord crer votre propre classe User, qui va tre sauvegarde dans la
base de donnes.
Listing 13-17

1
2
3
4
5
6
7
8
9
10
11
12
13

// src/Acme/UserBundle/Entity/User.php
namespace Acme\UserBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class User implements UserInterface
{
/**
* @ORM\Column(type="string", length=255)
*/
protected $username;

4. http://api.symfony.com/2.6/Symfony/Component/Security/Core/User/User.html
5. https://github.com/FriendsOfSymfony/FOSUserBundle

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 187

14
15 }

// ...

Pour ce qui concerne le systme de scurit, la seule exigence est que la classe User implmente l'interface
UserInterface6. Cela signifie que le concept d' utilisateur peut tre n'importe quoi, pour peu qu'il
implmente cette interface.
New in version 2.1: Dans Symfony 2.1, la mthode equals a t retire de la UserInterface.
Si vous avez besoin de surcharger l'implmentation par dfaut de la logique de comparaison,
implmentez la nouvelle interface EquatableInterface7.

L'objet User sera srialis et sauvegard dans la session lors des requtes, il est donc recommand
d'implmenter l'interface Serializable8 dans votre classe User. Cela est spcialement important si
votre classe User a une classe parente avec des proprits prives.

Ensuite, il faut configurer le fournisseur d'utilisateur entity (entity user provider), le pointer vers la
classe User :
Listing 13-18

1 # app/config/security.yml
2 security:
3
providers:
4
main:
5
entity: { class: Acme\UserBundle\Entity\User, property: username }

Avec l'introduction de ce nouveau fournisseur, le systme d'authentification va tenter de charger un objet


User depuis la base de donnes en utilisant le champ username de cette classe.
Cet exemple ne vous montre que les principes de base du fournisseur entity. Pour un exemple
complet et fonctionnel, veuillez lire Comment charger les utilisateurs depuis la base de donnes (le
fournisseur d'Entit).

Pour en apprendre plus sur comment crer votre propre fournisseur (par exemple si vous devez charger
des utilisateurs depuis un service Web), reportez-vous Comment crer un Fournisseur d'Utilisateur
personnalis.

Encoder les mots de passe


Jusqu' maintenant, afin de garder a simple, les mots de passe des utilisateurs ont tous t conservs au
format texte (qu'ils soient sauvegards dans un fichier de configuration ou dans la base de donnes). Il est
clair que dans une vraie application, vous allez vouloir encoder les mots de passe de vos utilisateurs pour
des raisons de scurit. Ceci est facile accomplir en mappant votre classe User avec un des nombreux
encodeurs intgrs.
Par exemple, pour rendre indchiffrables les mots de passe de vos utilisateurs en utilisant sha1, suivez les
instructions suivantes :
Listing 13-19

6. http://api.symfony.com/2.6/Symfony/Component/Security/Core/User/UserInterface.html
7. http://api.symfony.com/2.6/Symfony/Component/Security/Core/User/EquatableInterface.html
8. http://php.net/manual/en/class.serializable.php

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 188

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# app/config/security.yml
security:
# ...
providers:
in_memory:
users:
ryan: { password: bb87a29949f3a1ee0559f8a57357487151281386, roles:
'ROLE_USER' }
admin: { password: 74913f5cd5f61ec0bcfdb775414c2fb3d161b620, roles:
'ROLE_ADMIN' }
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: sha1
iterations: 1
encode_as_base64: false

En spcifiant les itrations 1 et le paramtre encode_as_base64 false, le mot de passe est


simplement encrypt en utilisant l'algorithme sha1 une fois, et sans aucun encodage additionnel. Vous
pouvez maintenant calculer le mot de passe soit programmatiquement (c'est--dire hash('sha1',
'ryanpass')) ou soit avec des outils en ligne comme functions-online.com9
Si vous crez vos utilisateurs dynamiquement (et que vous les sauvegardez dans une base de donnes),
vous pouvez rendre l'algorithme de hachage plus complexe puis utiliser un objet d'encodage de mot de
passe pour vous aider encoder les mots de passe. Par exemple, supposez que votre objet User est un
Acme\UserBundle\Entity\User (comme dans l'exemple ci-dessus). D'abord, configurez l'encodeur pour
cet utilisateur :
Listing 13-20

1 # app/config/security.yml
2 security:
3
# ...
4
5
encoders:
6
Acme\UserBundle\Entity\User: sha512

Dans cet exemple, nous utilisons L'algorithme plus puissant sha512. Aussi, comme nous avons
uniquement spcifi l'algorithme (sha512) sous forme de chane de caractres, le systme va par dfaut
hacher votre mot de passe 5000 fois de suite et ensuite l'encoder en base64. En d'autres termes, le mot de
passe a t trs fortement obscurci pour ne pas qu'il puisse tre dcod (c'est--dire que vous ne pouvez
pas retrouver le mot de passe depuis le mot de passe hach).
New in version 2.2: Depuis Symfony 2.2 vous pouvez galement utiliser l'encodeur de mot de passe
PBKDF2.

Si vous avez une sorte de formulaire d'enregistrement pour les utilisateurs, vous devez pouvoir gnrer un
mot de passe hach pour pouvoir le sauvegarder. Peu importe l'algorithme que vous avez configur pour
votre objet User, le mot de passe hach peut toujours tre dtermin de la manire suivante depuis un
contrleur :
Listing 13-21

1 $factory = $this->get('security.encoder_factory');
2 $user = new Acme\UserBundle\Entity\User();
3

9. http://www.functions-online.com/sha1.html

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 189

4 $encoder = $factory->getEncoder($user);
5 $password = $encoder->encodePassword('ryanpass', $user->getSalt());
6 $user->setPassword($password);

Rcuprer l'objet User


Aprs l'authentification, l'objet User correspondant l'utilisateur courant peut tre rcupr via le service
security.context. Depuis un contrleur, cela ressemble a:
Listing 13-22

1 public function indexAction()


2 {
3
$user = $this->get('security.context')->getToken()->getUser();
4 }

Dans un contrleur, vous pouvez utiliser le raccourci suivant :


Listing 13-23

1 public function indexAction()


2 {
3
$user = $this->getUser();
4 }

Les utilisateurs anonymes sont techniquement authentifis, ce qui veut dire que la mthode
isAuthenticated() sur un objet d'utilisateur anonyme va retourner true. Pour vrifier si un
utilisateur est vraiment authentifi, vrifiez si l'utilisateur a le rle IS_AUTHENTICATED_FULLY.

Dans un template Twig, cet objet est accessible via la cl app.user, qui appelle la mthode
GlobalVariables::getUser()10 :
Listing 13-24

1 <p>Username: {{ app.user.username }}</p>

Utiliser plusieurs fournisseurs d'utilisateurs


Chaque mcanisme d'authentification (par exemple authentification HTTP, formulaire de connexion,
etc...) utilise exactement un fournisseur d'utilisateur (user provider), et va utiliser par dfaut le premier
fournisseur d'utilisateurs dclar. Mais que faire si vous voulez dclarer quelques utilisateurs via la
configuration et le reste des utilisateurs dans la base de donnes? C'est possible en crant un fournisseur
qui lie les 2 fournisseurs ensemble :
Listing 13-25

1 # app/config/security.yml
2 security:
3
providers:
4
chain_provider:
5
chain :
6
providers: [in_memory, user_db]
7
in_memory:
8
memory:
9
users:
10
foo: { password: test }

10. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/Templating/GlobalVariables.html#getUser()

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 190

11
12

user_db:
entity: { class: Acme\UserBundle\Entity\User, property: username }

Maintenant, tous les mcanismes d'authentification vont utiliser le chain_provider, car c'est le premier
spcifi. Le chain_provider va essayer de charger les utilisateurs depuis les fournisseurs in_memory et
user_db.
Si vous n'avez pas de raison de sparer vos utilisateurs in_memory des utilisateurs user_db, vous
pouvez accomplir cela facilement en combinant les 2 sources dans un seul fournisseur :
Listing 13-26

1 # app/config/security.yml
2 security:
3
providers:
4
main_provider:
5
users:
6
foo: { password: test }
7
entity: { class: Acme\UserBundle\Entity\User, property: username }

Vous pouvez configurer le pare-feu ou des mcanismes individuels d'authentification afin qu'ils utilisent
un fournisseur spcifique. Encore une fois, le premier fournisseur sera toujours utilis, sauf si vous en
spcifiez un explicitement :
Listing 13-27

1 # app/config/security.yml
2 security:
3
firewalls:
4
secured_area:
5
# ...
6
provider: user_db
7
http_basic:
8
realm: "Secured Demo Area"
9
provider: in_memory
10
form_login: ~

Dans cet exemple, si un utilisateur essaie de se connecter via l'authentification HTTP, le systme utilisera
le fournisseur d'utilisateurs in_memory. Mais si l'utilisateur essaie de se connecter via le formulaire de
connexion, le fournisseur user_db sera utilis (car c'est celui par dfaut du pare-feu).
Pour plus d'informations propos des fournisseurs d'utilisateurs et de la configuration des pare-feu,
veuillez vous reporter Configuration de rfrence de la Scurit.

Les rles
La notion de rle est au centre du processus d'autorisation. Chaque utilisateur se fait assigner un
groupe de rles et chaque ressource ncessite un ou plusieurs rles. Si un utilisateur a les rles requis,
l'accs est accord. Sinon, l'accs est refus.
Les rles sont assez simples, et sont en fait des chanes de caractres que vous crez et utilisez au
besoin (mme si les rles sont des objets en interne). Par exemple, si vous dsirez limiter l'accs la
section d'administration du blog de votre site web, vous pouvez protger cette section en utilisant un
rle ROLE_BLOG_ADMIN. Ce rle n'a pas besoin d'tre dfini quelque part - vous n'avez qu' commencer
l'utiliser.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 191

Tous les rles doivent commencer par le prfixe ROLE_ afin d'tre grs par Symfony2. Si vous
dfinissez vos propres rles avec une classe Role ddie (plus avanc), n'utilisez pas le prfixe
ROLE_.

Rles hirarchiques
Au lieu d'associer plusieurs rles aux utilisateurs, vous pouvez dfinir des rgles d'hritage de rle en
crant une hirarchie de rles :
Listing 13-28

1 # app/config/security.yml
2 security:
3
role_hierarchy:
4
ROLE_ADMIN:
ROLE_USER
5
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

Dans la configuration ci-dessus, les utilisateurs avec le rle ROLE_ADMIN vont aussi avoir le rle
ROLE_USER. Le rle ROLE_SUPER_ADMIN a les rles ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH et ROLE_USER
(hrit de ROLE_ADMIN).

Se dconnecter
Gnralement, vous dsirez aussi que vos utilisateurs puissent se dconnecter. Heureusement, le pare-feu
peut prendre a en charge automatiquement lorsque vous activez le paramtre de configuration logout :
Listing 13-29

1 # app/config/security.yml
2 security:
3
firewalls:
4
secured_area:
5
# ...
6
logout:
7
path:
/logout
8
target: /
9
# ...

Une fois que c'est configur au niveau de votre pare-feu, un utilisateur qui accdera /logout (ou quelle
que soit la configuration de path que vous avez) sera dconnect. L'utilisateur sera redirig la page
d'accueil (la valeur du paramtre target). Les 2 paramtres de configuration path et target ont comme
valeur par dfaut ce qui est dfini ici. En d'autres termes, sauf si vous voulez les changer, vous pouvez les
omettre compltement et ainsi rduire votre configuration :
Listing 13-30

1 logout: ~

Veuillez noter que vous n'aurez pas implmenter un contrleur pour l'URL /logout car le pare-feu se
charge de tout. Vous devez toutefois crer une route afin de l'utiliser pour gnrer l'URL :
Depuis Symfony 2.1, vous devez avoir une route qui correspond votre chemin de dconnexion.
Sans route, vous ne pourrez pas vous dconnecter.

Listing 13-31

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 192

1 # app/config/routing.yml
2 logout:
3
pattern:
/logout

Une fois qu'un utilisateur s'est dconnect, il sera redirig l'URL dfinie par le paramtre target (par
exemple homepage). Pour plus d'informations sur la configuration de la dconnexion, veuillez lire Security
Configuration Reference.

Contrle d'accs dans les templates


Si vous dsirez vrifier dans un template si un utilisateur possde un rle donn, utilisez la fonction helper
intgre :
Listing 13-32

1 {% if is_granted('ROLE_ADMIN') %}
2
<a href="...">Supprimer</a>
3 {% endif %}

Si vous utilisez cette fonction et que vous ne vous trouvez pas une URL pour laquelle un pare-feu
est actif, une exception sera lance. Encore une fois, c'est toujours une bonne ide d'avoir un parefeu qui couvre toutes les URLs (comme c'est montr dans ce chapitre).

Contrle d'accs dans les Contrleurs


Si vous dsirez vrifier dans un contrleur si l'utilisateur courant possde un rle, utilisez la mthode
isGranted du contexte de scurit:
Listing 13-33

1 public function indexAction()


2 {
3
// show different content to admin users
4
if ($this->get('security.context')->isGranted('ROLE_ADMIN')) {
5
// Load admin content here
6
}
7
// load other regular content here
8 }

Un pare-feu doit tre actif, sinon une exception sera lance lors de l'appel la mthode isGranted.
Rfrez-vous aux notes ci-dessus par rapport aux templates pour plus de dtails.

Usurper l'identit d'un utilisateur


Parfois, il peut tre utile de pouvoir passer d'un utilisateur un autre sans avoir se dconnecter et se
reconnecter (par exemple si vous tes en train de dbugguer ou de comprendre un bug qu'un utilisateur
obtient, mais que vous ne pouvez pas reproduire). Cela peut tre facilement ralis en activant l'auditeur
(listener) switch_user du pare-feu :
Listing 13-34

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 193

1 # app/config/security.yml
2 security:
3
firewalls:
4
main:
5
# ...
6
switch_user: true

Pour changer d'utilisateur, il suffit d'ajouter la chane de requte le paramtre _switch_user et le nom
d'utilisateur comme valeur l'URL en cours :
Listing 13-35

1 http://example.com/somewhere?_switch_user=thomas

Pour revenir l'utilisateur initial, utilisez le nom d'utilisateur spcial _exit:


Listing 13-36

1 http://example.com/somewhere?_switch_user=_exit

Bien sr, cette fonctionnalit ne doit tre accessible qu' un petit groupe d'utilisateurs. Par dfaut, l'accs
est limit aux utilisateurs ayant le rle ROLE_ALLOWED_TO_SWITCH. Le nom de ce rle peut tre modifi
grce au paramtre role. Pour plus de scurit, vous pouvez aussi changer le nom du paramtre de
configuration grce au paramtre parameter:
Listing 13-37

1 # app/config/security.yml
2 security:
3
firewalls:
4
main:
5
// ...
6
switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }

Authentification sans tat


Par dfaut, Symfony2 s'appuie sur cookie (la Session) pour garder le contexte de scurit d'un utilisateur.
Mais si vous utilisez des certificats ou l'authentification HTTP par exemple, la persistance n'est pas
ncessaire, car l'identit est disponible chaque requte. Dans ce cas, et si vous n'avez pas besoin de
sauvegarder quelque chose entre les requtes, vous pouvez activer l'authentification sans tat (stateless
authentication), ce qui veut dire qu'aucun cookie ne sera jamais cr par Symfony2 :
Listing 13-38

1 # app/config/security.yml
2 security:
3
firewalls:
4
main:
5
http_basic: ~
6
stateless: true

Si vous utilisez un formulaire de connexion, Symfony2 va crer un cookie mme si vous avez
configur stateless true.

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 194

Utilitaires
New in version 2.2: Les classes StringUtils et SecureRandom ont t ajoutes dans Symfony 2.2

Le composant de Scurit Symfony est fourni avec un ensemble d'utilitaires pratiques lis la scurit.
Ces utilitaires sont utiliss par Symfony, mais vous devriez aussi les utiliser pour rsoudre les problmes
qu'ils traitent.

Comparer des chaines de caractres


Le temps pris pour comparer deux chaines de caractres dpend de leurs diffrences. Cela peut tre utilis
par un attaquant lorsque les deux chaines reprsentent un mot de passe par exemple; cela s'appelle une
attaque temporelle11.
En interne, pour comparer deux mots de passe, Symfony utilise un algorithme en temps constant; vous
pouvez utiliser la mme stratgie dans votre propre code grce la classe StringUtils12:
Listing 13-39

1 use Symfony\Component\Security\Core\Util\StringUtils;
2
3 // est-ce password1 est gal password2 ?
4 $bool = StringUtils::equals($password1, $password2);

Gnrer un nombre alatoire scuris


Chaque fois que vous avez besoin de gnrer un nombre alatoire scuris, nous vous incitons fortement
utiliser la classe SecureRandom13:
Listing 13-40

1 use Symfony\Component\Security\Core\Util\SecureRandom;
2
3 $generator = new SecureRandom();
4 $random = $generator->nextBytes(10);

La mthode nextBytes()14 retourne une chaine de caractres numrique d'une longueur gale au
nombre pass en argument (10 dans l'exemple ci-dessus).
La classe SecureRandom est plus efficace lorsque OpenSSL est install, mais s'il n'est pas disponible, elle
se rabat sur un algorithme interne qui a besoin d'un fichier pour l'alimenter. Contentez-vous de passer le
nom du fichier en argument pour l'activer:
Listing 13-41

1 $generator = new SecureRandom('/some/path/to/store/the/seed.txt');


2 $random = $generator->nextBytes(10);

Vous pouvez aussi accder une instance alatoire scurise directement depuis le conteneur
d'injection de dpendance de Symfony. Son nom est security.secure_random.

11. http://fr.wikipedia.org/wiki/Attaque_temporelle
12. http://api.symfony.com/2.6/Symfony/Component/Security/Core/Util/StringUtils.html
13. http://api.symfony.com/2.6/Symfony/Component/Security/Core/Util/SecureRandom.html
14. http://api.symfony.com/2.6/Symfony/Component/Security/Core/Util/SecureRandom.html#nextBytes()

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 195

Derniers mots
La scurit peut tre un problme complexe rsoudre correctement dans une application.
Heureusement, le composant de scurit de Symfony se base un modle bien prouv bas sur
l'authentification et l'autorisation. L'authentification, qui arrive toujours en premier, est prise en charge
par le pare-feu dont la responsabilit est de dterminer l'identit des utilisateurs grce diffrentes
mthodes (par exemple l'authentification HTTP, les formulaires de connexion, etc.). Dans le cookbook,
vous trouverez des exemples d'autres mthodes pour prendre en charge l'authentification, incluant une
manire d'implmenter la fonction de cookie se souvenir de moi ( remember me ),
Une fois l'utilisateur authentifi, la couche d'autorisation peut dterminer si l'utilisateur a accs ou non
des ressources spcifiques. Le plus souvent, des rles sont appliqus aux URLs, classes ou mthodes
et si l'utilisateur courant ne possde pas ce rle, l'accs est refus. La couche d'autorisation est toutefois
beaucoup plus complexe, et suit un systme de vote afin que plusieurs entits puissent dterminer si
l'utilisateur courant devrait avoir accs une ressource donne.
Apprenez en plus sur la scurit et sur d'autres sujets dans le cookbook.

Apprenez plus grce au Cookbook

Forcer HTTP/HTTPS
Blacklister des utilisateurs par adresse IP grce un lecteur personnalis
Liste d'accs de contrle (ACLs)
Comment ajouter la fonctionnalit de login Se souvenir de moi

PDF brought to you by


generated on January 14, 2015

Chapter 13: La scurit | 196

Chapter 14

Le Cache HTTP
L'essence d'une application web riche est d'tre dynamique. Peu importe l'efficacit de votre application,
le traitement d'une requte sera toujours plus important que l'envoi d'une page statique.
Pour la plupart des applications web, cela ne pose pas de problme. Symfony2 est d'une rapidit
foudroyante, et moins que vous ne fassiez de srieux remaniements, chaque requte sera traite
rapidement sans trop stresser votre serveur.
Mais si la frquentation de votre site augmente, ce traitement peut devenir un problme. Le processus
qui s'effectue chaque requte peut tre excut une unique fois. C'est exactement l'objectif de la mise
en cache.

La Mise en cache
Le moyen le plus efficace d'amliorer les performances d'une application est de mettre en cache
l'intgralit d'une rponse pour ne plus avoir rappeler l'application pour les requtes suivantes. Bien sr,
ce n'est pas toujours possible pour les sites web fortement dynamiques, ou peut tre que si. A travers ce
chapitre, vous verrez comment fonctionne le systme de cache de Symfony2 et en quoi c'est la meilleure
approche possible.
Le systme de cache de Symfony2 est diffrent, car il se base sur la simplicit et la puissance du cache
HTTP tel qu'il est dfini dans la spcification HTTP. Au lieu de rinventer un processus de mise en cache,
Symfony2 adopte la norme qui dfinit la communication de base sur le Web. Une fois que vous avez
compris les fondamentaux de la validation HTTP et de l'expiration de la mise en cache, vous serez prt
matriser le systme de cache de Symfony2.
Dans le but de comprendre comment mettre en cache avec Symfony, nous allons parcourir ce sujet en 4
tapes :
1. Une passerelle de cache, ou reverse proxy, est une couche indpendante qui se trouve devant
votre application. La passerelle met en cache les rponses telles qu'elles sont retournes par
l'application et rpond aux requtes avec les rponses qui sont en cache avant qu'elles
n'atteignent l'application. Symfony2 possde sa propre passerelle par dfaut, mais n'importe
quelle autre peut tre galement utilise.
2. Les enttes du cache HTTP sont utiliss pour communiquer avec la passerelle de cache et tout
autre cache entre votre application et le client. Symfony2 en propose par dfaut et fournit une
interface puissante pour interagir avec eux.
PDF brought to you by
generated on January 14, 2015

Chapter 14: Le Cache HTTP | 197

3. L'expiration et la validation HTTP sont les deux modles utiliss pour dterminer si le contenu
d'un cache est valide (peut tre rutilis partir du cache) ou prim (doit tre rgnr par
l'application).
4. Les Edge Side Includes (ESI) autorisent le cache HTTP mettre en cache des fragments de
pages (voir des fragments imbriqus) de faon indpendante. Avec les ESI, vous pouvez mme
mettre en cache une page entire pendant 60 minutes, mais un bloc imbriqu dans cette page
uniquement 5 minutes.
Puisque la mise en cache via HTTP n'est pas spcifique Symfony, de nombreux articles existent ce
sujet. Si vous n'tes pas familier avec la mise cache HTTP, nous vous recommandons fortement de lire
l'article de Ryan Tomayko Things Caches Do1. Le tutoriel de Mark Nottingham, Cache Tutorial2, est
galement une ressource trs complte sur ce sujet.

La mise en cache avec la Passerelle de Cache


Lors d'une mise en cache via HTTP, le cache est compltement spar de votre application. Il est plac
entre votre application et le client qui effectue les requtes.
Le travail du cache est d'accepter les requtes du client et de les transmettre votre application. Le cache
recevra aussi en retour des rponses de votre application et les enverra au client. Le cache est au milieu (
middle-man ) dans ce jeu de communication requte-rponse entre le client et votre application.
Lors d'une communication, le cache stockera toutes les rponses qu'il estime stockables (voir
Introduction la mise en cache avec HTTP). Si la mme ressource est demande, le cache renvoie le
contenu mis en cache au client, en ignorant entirement l'application.
Ce type de cache est connu sous le nom de passerelle de cache HTTP. Beaucoup d'autres solutions
existent telles que Varnish3, Squid in reverse proxy mode4 et le reverse proxy de Symfony2.

Les types de caches


Mais une passerelle de cache ne possde pas qu'un seul type de cache. Les enttes de cache HTTP
envoyes par votre application sont interprtes par trois diffrents types de cache :
Le cache du navigateur : tous les navigateurs ont leur propre cache qui est utile quand un
utilisateur demande la page prcdente ou des images et autres mdias. Le cache du navigateur
est un cache priv, car les ressources mises en cache ne sont partages avec personne d'autre.
Le cache proxy : un proxy est un cache partag car plusieurs personnes peuvent tre derrire
un seul proxy. Il est habituellement install par les entreprises et les FAIs pour diminuer le
temps de rponse des sites et la consommation des ressources rseau.
Passerelle de cache : comme un proxy, ce systme de cache est galement partag, mais du ct
serveur. Install par des administrateurs rseau, il permet aux sites d'tre plus extensibles, srs
et performants.
Les passerelles de cache peuvent tre dsignes comme des reverse proxy cache, surrogate
cache ou mme des acclrateurs HTTP.

1. http://tomayko.com/writings/things-caches-do
2. http://www.mnot.net/cache_docs/
3. https://www.varnish-cache.org/
4. http://wiki.squid-cache.org/SquidFaq/ReverseProxy

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 198

La notion de cache priv par rapport au cache partag sera explique plus en dtail lorsque la mise
en cache des contenus lis exactement un utilisateur (les informations sur un compte utilisateur
par exemple) sera aborde.

Toutes les rponses de l'application iront communment dans un ou deux des deux premiers types de
cache. Ces systmes ne sont pas sous votre contrle, mais suivent les directives du cache HTTP dfinies
dans les rponses.

Symfony2 Reverse Proxy


Symfony2 contient un reverse proxy (aussi appel passerelle de cache) crit en PHP. Activez-le et les
rponses de votre application qui peuvent tre mise en cache seront immdiatement stockes. L'installer
est aussi trs simple. Chaque nouvelle application Symfony2 contient un noyau pr-configur
(AppCache) qui encapsule le noyau par dfaut (AppKernel). Le cache kernel (cache du noyau) est le
reverse proxy.
Pour activer le mcanisme de cache, il faut modifier le code du contrleur principal pour qu'il utilise le
cache kernel:
Listing 14-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
require_once __DIR__.'/../app/AppCache.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
// encapsule le AppKernel par dfaut avec AppCache
$kernel = new AppCache($kernel);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Le cache kernel se comportera immdiatement comme un reverse proxy en mettant en cache les
rponses de l'application et en les renvoyant au client.
Le cache kernel a une mthode spciale getLog() qui retourne une chane de caractres dcrivant
ce qui se passe dans la couche du cache. Dans l'environnement de dveloppement, il est possible
de l'utiliser pour du dbogage ou afin de valider votre stratgie de mise en cache:
Listing 14-2

1 error_log($kernel->getLog());

L'objet AppCache a une configuration par dfaut, mais peut tre reconfigur finement grce une srie
d'options que vous pouvez paramtrer en surchargeant la mthode getOptions()5:
Listing 14-3

1 // app/AppCache.php
2 use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
3
4 class AppCache extends HttpCache

5. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.html#getOptions()

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 199

5 {
6
7
8
9
10
11
12
13
14
15
16
17
18 }

protected function getOptions()


{
return array(
'debug'
'default_ttl'
'private_headers'
'allow_reload'
'allow_revalidate'
'stale_while_revalidate'
'stale_if_error'
);
}

=>
=>
=>
=>
=>
=>
=>

false,
0,
array('Authorization', 'Cookie'),
false,
false,
2,
60,

A moins d'tre surcharge dans getOptions(), l'option debug est par dfaut gale celle de l'objet
AppKernel encapsul.

Voici une liste des principales options :


default_ttl : Le nombre de secondes pendant lesquelles une entre du cache doit tre
considre comme valide quand il n'y a pas d'information explicite fournie dans une
rponse. Une valeur explicite pour les enttes Cache-Control ou Expires surcharge cette
valeur (par dfaut : 0);
private_headers : Ensemble d'enttes de requte qui dclenche le comportement priv du
Cache-Control pour les rponses qui ne prcisent pas explicitement si elle sont publiques ou
prives via une directive du Cache-Control. (par dfaut : Authorization et Cookie);
allow_reload : Dfinit si le client peut forcer ou non un rechargement du cache en incluant
une directive du Cache-Control no-cache dans la requte. Dfinissez-la true pour la
conformit avec la RFC 2616 (par dfaut : false);
allow_revalidate : Dfinit si le client peut forcer une revalidation du cache en incluant
une directive de Cache-Control max-age=0 dans la requte. Dfinissez-la true pour la
conformit avec la RFC 2616 (par dfaut : false);
stale_while_revalidate : Spcifie le nombre de secondes par dfaut (la granularit est la
seconde parce que le TTL de la rponse est en seconde) pendant lesquelles le cache peut
renvoyer une rponse prime alors que la nouvelle rponse est calcule en arrire-plan (par
dfaut : 2). Ce paramtre est surcharg par l'extension HTTP stale-while-revalidate du
Cache-Control (cf. RFC 5861);
stale_if_error : Spcifie le nombre de secondes par dfaut (la granularit est la seconde)
pendant lesquelles le cache peut renvoyer une rponse prime quand une erreur est
rencontre (par dfaut : 60). Ce paramtre est surcharg par l'extension HTTP stale-iferror du Cache-Control (cf. RFC 5961).
Si le paramtre debug est true, Symfony2 ajoute automatiquement l'entte X-Symfony-Cache la
rponse contenant des informations utiles propos des caches hits (utilisation du cache) et misses
(page ou rponse non prsente en cache).

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 200

Passer d'un Reverse Proxy un autre


Le reverse proxy de Symfony2 est un formidable outil lors de la phase de dveloppement de votre
site web ou lors d'un dploiement sur des serveurs mutualiss sur lesquels il n'est pas possible
d'installer d'autres outils que ceux proposs par PHP. Mais il n'est pas aussi performant que des
proxy crits en C. C'est pourquoi il est fortement recommand d'utiliser Varnish ou Squid sur les
serveurs de production si possible. La bonne nouvelle est qu'il est trs simple de passer d'un proxy
un autre sans qu'aucune modification ne soit ncessaire dans le code. Vous pouvez commencez
avec le reverse proxy de Symfony2 puis le mettre jour plus tard vers Varnish quand votre trafic
augmentera.
Pour plus d'informations concernant Varnish avec Symfony2, veuillez- vous reportez au chapitre
du cookbook Comment utiliser Varnish.

Les performances du reverse proxy de Symfony2 ne sont pas lies la complexit de votre
application. C'est parce que le noyau de l'application n'est dmarr que quand la requte lui est
transmise.

Introduction la mise en cache avec HTTP


Pour tirer parti des couches de gestion du cache, l'application doit tre capable de communiquer quelles
rponses peuvent tre mises en cache et les rgles, qui dcident quand et comment le cache devient
obsolte. Cela se fait en dfinissant des enttes de cache HTTP dans la rponse.
Il faut garder l'esprit que HTTP n'est rien d'autre que le langage (un simple langage texte) que
les clients web (les navigateurs par exemple) et les serveurs utilisent pour communiquer entre eux.
Parler de mise en cache HTTP revient parler de la partie du langage qui permet aux clients et aux
serveurs d'changer les informations relatives la gestion du cache.

HTTP dfinit quatre enttes de cache que nous dtaillons ici :

Cache-Control
Expires
ETag
Last-Modified

L'entte le plus important et le plus versatile est l'entte Cache-Control qui est en ralit une collection
d'informations diverses sur le cache.
Tous ces enttes seront compltement dtaills dans la section HTTP Expiration et Validation.

L'entte Cache-Control
Cet entte est unique du fait qu'il contient non pas une, mais un ensemble vari d'informations sur la
possibilit de mise en cache d'une rponse. Chaque information est spare par une virgule :
Listing 14-4

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 201

1 Cache-Control: private, max-age=0, must-revalidate


2
3 Cache-Control: max-age=3600, must-revalidate

Symfony fournit une abstraction du Cache-Control pour faciliter sa gestion:


Listing 14-5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// ...
use Symfony\Component\HttpFoundation\Response;
$response = new Response();

// marque la rponse comme publique ou prive


$response->setPublic();
$response->setPrivate();
// dfinit l'ge max des caches privs ou des caches partags
$response->setMaxAge(600);
$response->setSharedMaxAge(600);
// dfinit une directive personnalise du Cache-Control
$response->headers->addCacheControlDirective('must-revalidate', true);

Rponse publique et rponse prive


Les passerelles de cache et les caches proxy sont considrs comme tant partags car leur contenu
est partag par plusieurs utilisateurs. Si une rponse spcifique un utilisateur est stocke par erreur dans
ce type de cache, elle pourrait tre renvoye plusieurs autres utilisateurs. Imaginez si les informations
concernant votre compte sont mises en cache et ensuite envoyes tous les utilisateurs suivants qui
souhaitent accder leur page de compte !
Pour grer cette situation, chaque rponse doit tre dfinie comme tant publique ou prive :
public: Indique que la rponse peut tre mise en cache, la fois,
par les caches privs et les caches publics;
private: Indique que toute la rponse concerne un unique
utilisateur et qu'elle ne doit pas tre stocke dans les caches publics.
Symfony considre par dfaut chaque rponse comme tant prive. Pour tirer parti des caches partags
(comme le reverse proxy de Symfony2), la rponse devra explicitement tre dfinie comme publique.

Mthodes sures
La mise en cache HTTP ne fonctionne qu'avec les mthodes sures (telles que GET et HEAD). tre
sr signifie que l'tat de l'application n'est jamais modifi par le serveur au moment de servir la requte
(il est bien sr possible de loguer des informations, mettre en cache des donnes, etc.). Cela a deux
consquences :
L'tat de l'application ne devrait jamais tre modifi en rpondant une requte GET ou
HEAD. Mme s'il n'y a pas de passerelle de cache, la prsence d'un cache proxy signifie
qu'aucune requte GET ou HEAD ne pourrait pas atteindre le serveur.
Ne pas mettre en cache les mthodes PUT, POST ou DELETE. Ces mthodes sont
normalement utilises pour changer l'tat de l'application (supprimer un billet de blog par

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 202

exemple). La mise en cache de ces mthodes empcherait certaines requtes d'atteindre et de


modifier l'application.

Rgles de mise en cache et configuration par dfaut


HTTP 1.1 permet de tout mettre en cache par dfaut moins qu'il n'y ait un entte Cache-Control
explicite. En pratique, la plupart des systmes de cache ne font rien quand les requtes contiennent un
cookie, ont un entte d'autorisation, utilisent une mthode non sure (i.e. PUT, POST, DELETE), ou
quand les rponses ont un code de redirection.
Symfony2 dfinit automatiquement une configuration de l'entte Cache-Control, quand aucun n'est
dfini par le dveloppeur, en suivant ces rgles :
Si aucun entte de cache n'est dfini (Cache-Control, Expires, ETag ou Last-Modified),
Cache-Control est dfini no-cache, ce qui veut dire que la rponse ne sera pas mise en
cache;
Si Cache-Control est vide (mais que l'un des autres enttes de cache est prsent) sa valeur est
dfinie private, must-revalidate;
Mais si au moins une directive Cache-Control est dfinie, et qu'aucune directive publique ou
private n'a t ajoute explicitement, Symfony2 ajoute la directive private automatiquement
(sauf quand s-maxage est dfini).

HTTP Expiration et Validation


La spcification HTTP dfinit deux modles de mise en cache :
Avec le modle d'expiration6, on spcifie simplement combien de temps une rponse doit
tre considre comme valide en incluant un entte Cache-Control et/ou Expires. Les
systmes de cache qui supportent l'expiration enverront la mme rponse jusqu' ce que la
version en cache soit expire et devienne invalide .
Quand une page est dynamique (c-a-d quand son contenu change souvent), le modle de
validation7 est souvent ncessaire. Avec ce modle, le systme de cache stocke la rponse, mais
demande au serveur chaque requte si la rponse est encore valide. L'application utilise un
identifiant unique (l'entte Etag) et/ou un timestamp (l'entte Last-Modified) pour vrifier si
la page a chang depuis sa mise en cache.
Le but de ces deux modles est de ne jamais gnrer deux fois la mme rponse en s'appuyant sur le
systme de cache pour stoker et renvoyer la rponse valide.

6. http://tools.ietf.org/html/rfc2616#section-13.2
7. http://tools.ietf.org/html/rfc2616#section-13.3

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 203

En lisant la spcification HTTP


La spcification HTTP dfinit un langage simple, mais puissant dans lequel les clients et les
serveurs peuvent communiquer. En tant que dveloppeur web, le modle requte-rponse est le
plus populaire. Malheureusement, le document de spcification - RFC 26168 - peut tre difficile
lire.
Il existe actuellement une tentative (HTTP Bis9) de rcriture du RFC 2616. Elle ne dcrit pas une
nouvelle version du HTTP mais clarifie plutt la spcification originale du HTTP. Elle est dcoupe
en sept parties ; tout ce qui concerne la gestion du cache se retrouve dans deux chapitres ddis
(P4 - Conditional Requests et P6 - Caching: Browser and intermediary caches10).
En tant que dveloppeur web, il est fortement recommand de lire la spcification. Sa clart et
sa puissance - mme plus dix ans aprs sa cration - est inestimable. Ne soyez pas rebut par
l'apparence du document - son contenu est beaucoup plus intressant que son aspect.

Expiration
Le modle d'expiration du cache est le plus efficace et le plus simple mettre en place et devrait tre
utilis ds que possible. Quand une rponse est mise en cache avec une directive d'expiration, le cache
stockera la rponse et la renverra directement sans solliciter l'application avant son expiration.
Ce modle est mis en oeuvre avec deux enttes HTTP presque identiques : Expires ou Cache-Control.

Expiration avec l'entte Expires


D'aprs la spcification HTTP, le champ de l'entte Expires donne la date ou le temps aprs laquelle
la rponse est considre comme invalide . L'entte Expires peut tre dfini avec la mthode
setExpires() de l'objet Response. Elle prend un objet DateTime en argument:
Listing 14-6

1 $date = new DateTime();


2 $date->modify('+600 seconds');
3
4 $response->setExpires($date);

L'entte HTTP rsultante sera :


Listing 14-7

1 Expires: Thu, 01 Mar 2011 16:00:00 GMT

La mthode setExpires() convertit automatiquement la date au format GMT comme demand


par la spcification.

Notez que dans toutes les versions HTTP prcdant la 1.1, le serveur d'origine n'tait pas oblig d'envoyer
l'entte Date. En consquence, le cache (par exemple le navigateur) pourrait tre oblig de consulter
l'horloge locale afin d'valuer l'entte Expires rendant ainsi le calcul de la dure de vie sensible aux
dcalages horaires. Une autre limitation de l'entte Expires est que la spcification dclare que les
serveurs HTTP/1.1 ne devraient pas envoyer des dates Expires de plus d'un an dans le futur .

8. http://tools.ietf.org/html/rfc2616
9. http://tools.ietf.org/wg/httpbis/
10. http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-12

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 204

Expiration avec l'entte Cache-Control


cause des limitations de l'entte Expires, bien souvent, il faut utiliser l'entte Cache-Control.
Rappelez-vous que l'entte Cache-Control est utilis pour spcifier une grande partie des directives de
cache. Pour le modle d'expiration, il y a deux directives, max-age et s-maxage. La premire est utilise
par tous les systmes de cache alors que la seconde n'est utilise que par les systmes de cache partags:
Listing 14-8

1
2
3
4
5
6

// Dfinir le nombre de secondes aprs lesquelles la rponse


// ne devrait plus tre considre comme valide
$response->setMaxAge(600);
// Idem mais uniquement pour les caches partags
$response->setSharedMaxAge(600);

L'entte Cache-Control devrait tre (il peut y avoir d'autres directives):


Listing 14-9

1 Cache-Control: max-age=600, s-maxage=600

Validation
S'il faut mettre jour une ressource ds qu'il y a un changement de donnes, le modle d'expiration ne
convient pas. Avec le modle d'expiration, l'application ne sera pas appele jusqu'au moment o le cache
devient invalide.
Le modle de validation du cache corrige ce problme. Dans ce modle, le cache continue de stocker les
rponses. La diffrence est que pour chaque requte, le cache demande l'application si la rponse en
cache est encore valide. Si la rponse en cache est toujours valide, l'application renvoie un code statut 304
et aucun contenu. Cela indique au cache qu'il est autoris renvoyer la rponse mis en cache.
Ce modle permet d'conomiser beaucoup de bande passante, car la mme rponse n'est pas envoye
deux fois au mme client (un code 304 est envoy la place). Si l'application est bien construite, il est
possible de dterminer le minimum de donnes ncessitant l'envoi de rponse 304 et aussi d'conomiser
des ressources CPU (voir ci-dessous pour un exemple d'implmentation).
Le code 304 signifie Not Modified (non modifi). C'est important, car avec ce code statut,
la rponse ne contient pas le contenu demand. Au lieu de cela, la rponse est simplement un
ensemble lger de directives qui informe le cache qu'il devrait utiliser la rponse stocke.

Comme avec le modle d'expiration, il y a deux diffrents types d'enttes HTTP qui peuvent tre utiliss
pour implmenter ce modle : ETag et Last-Modified.

Validation avec l'entte ETag


L'entte ETag est une chane de caractres (appele entity-tag ) qui identifie de faon unique une
reprsentation de la ressource appele. Il est entirement gnr et dfini par votre application de telle
sorte que vous pouvez spcifier, par exemple, si la ressource /about, stocke en cache, est jour avec
ce que votre application retourne. Un ETag est similaire une empreinte et est utilis pour comparer
rapidement si deux versions diffrentes d'une ressource sont quivalentes. Comme une empreinte,
chaque ETag doit tre unique pour toutes les reprsentations de la mme ressource.
Voici une implmentation simple qui gnre l'entte ETag depuis un md5 du contenu:
Listing 14-10

1 public function indexAction()


2 {

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 205

3
$response = $this->render('MyBundle:Main:index.html.twig');
4
$response->setETag(md5($response->getContent()));
5
$response->setPublic(); // permet de s'assurer que la rponse est publique, et qu'elle
6 peut donc tre cache
7
$response->isNotModified($this->getRequest());
8
9
return $response;
}

La mthode isNotModified()11 method:Symfony\Component\HttpFoundation\Response::isNotModified


compare le ETag envoy avec la requte avec celui dfini dans l'objet Response. S'ils sont identiques, la
mthode dfinit automatiquement le code de l'objet Response comme 304.
Cet algorithme est assez simple et trs gnrique, mais il implique de crer entirement l'objet Response
avant de pouvoir calculer l'entte ETag, ce qui n'est pas optimal. En d'autres termes, cette approche
conomise la bande passante, mais pas l'utilisation du CPU.
Dans la section Optimiser son code avec le modle de validation du cache, vous verrez comment le modle
de validation peut tre utilis plus intelligemment pour dterminer la validit d'un cache sans faire autant
de travail.
Symfony2 supporte aussi les ETags moins robustes en dfinissant le second argument true pour
la mthode setETag()12.

Validation avec l'entte Last-Modified


L'entte Last-Modified est la seconde forme de validation. D'aprs la spcification HTTP, le champ de
l'entte Last-Modified indique la date et l'heure laquelle le serveur d'origine croit que la reprsentation
a t modifie pour la dernire fois . En d'autres termes, l'application dcide si oui ou non le contenu du
cache a t mis jour, en se basant sur le fait que, si oui ou non le cache a t mis jour depuis que la
rponse a t mise en cache.
Par exemple, vous pouvez utiliser la date de dernire mise jour de tous les objets ncessitant de calculer
le rendu de la ressource comme valeur de l'entte Last-Modified:
Listing 14-11

1 public function showAction($articleSlug)


2 {
3
// ...
4
5
$articleDate = new \DateTime($article->getUpdatedAt());
6
$authorDate = new \DateTime($author->getUpdatedAt());
7
8
$date = $authorDate > $articleDate ? $authorDate : $articleDate;
9
10
$response->setLastModified($date);
11
// Dfinit la rponse comme publique. Sinon elle sera prive par dfaut.
12
$response->setPublic();
13
14
if ($response->isNotModified($this->getRequest())) {
15
return $response;
16
}
17

11. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html#isNotModified()
12. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html#setETag()

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 206

18
19
20
21 }

// ... ajoutez du code ici pour remplir la rponse avec le contenu complet
return $response;

La mthode isNotModified()13 compare l'entte If-Modified-Since envoy par la requte avec l'entte
Last-Modified dfini dans la rponse. S'ils sont quivalents, l'objet Response aura un code status 304.
L'entte de la requte If-Modified-Since est gal l'entte de la dernire rponse Last-Modified
du client pour une ressource donne. C'est grce cela que le client et le serveur communiquent et
constatent ou non si la ressource a t mise jour depuis qu'elle est en cache.

Optimiser son code avec le modle de validation du cache


Le but principal de toutes les stratgies de mise en cache est de diminuer la charge de l'application.
Autrement dit, moins l'application aura travailler pour renvoyer un status 304, mieux ce sera. La
mthode Response::isNotModified() fait exactement a en exposant un modle simple et efficace:
Listing 14-12

1 use Symfony\Component\HttpFoundation\Response;
2
3 public function showAction($articleSlug)
4 {
5
// Rcupre le minimum d'informations pour calculer
6
// l'ETag ou la dernire valeur modifie (Last-Modified value)
7
// (bas sur l'objet Request, les donnes sont recueillies
8
// d'une base de donnes ou d'un couple cl-valeur
9
// par exemple)
10
$article = ...;
11
12
// Cre un objet Response avec un entte ETag
13
// et/ou un entte Last-Modified
14
$response = new Response();
15
$response->setETag($article->computeETag());
16
$response->setLastModified($article->getPublishedAt());
17
18
// Dfinit la rponse comme publique. Sinon elle sera prive par dfaut.
19
$response->setPublic();
20
21
// Vrifie que l'objet Response n'est pas modifi
22
// pour un objet Request donn
23
if ($response->isNotModified($this->getRequest())) {
24
// Retourne immdiatement un objet 304 Response
25
return $response;
26
} else {
27
// faire plus de travail ici - comme rcuprer plus de donnes
28
$comments = ...;
29
30
// ou formatter un template avec la $response dj existante
31
return $this->render(
32
'MyBundle:MyController:article.html.twig',
33
array('article' => $article, 'comments' => $comments),
34
$response
35
);

13. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html#isNotModified()

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 207

36
37 }

Quand l'objet Response n'est pas modifi, la mthode isNotModified() dfinit automatiquement le
code 304, enlve le contenu et les enttes qui ne doivent pas tre prsents pour un status 304 (voir la
setNotModified()14).

Faire varier la Response


Jusqu'ici, nous avons suppos que chaque URI est une reprsentation unique de la ressource cible. Par
dfaut, la mise en cache HTTP est faite en donnant l'URI de la ressource comme cl de cache. Si deux
personnes demandent la mme URI d'une ressource qui peut tre mise en cache, la deuxime personne
recevra la version qui est dans le cache.
Dans certains cas, ce n'est pas suffisant et des versions diffrentes de la mme URI ont besoin d'tre mises
en cache en fonction des valeurs d'un ou plusieurs enttes. Par exemple, si les pages sont compresses
parce que le client le supporte, n'importe quelle URI a deux reprsentations : une quand le client accepte
la compression, l'autre quand le client ne l'accepte pas. Cette dtermination est faite grce la valeur de
l'entte Accept-Encoding.
Dans ce cas, le cache doit contenir une version compresse et une version non compresse de la rponse
pour une URI particulire et les envoyer en fonction de la valeur Accept-Encoding de la requte. Cela
est possible en utilisant l'entte Vary de la rponse, qui est une liste des diffrents enttes spars par des
virgules dont les valeurs dfinissent une reprsentation diffrente de la mme ressource.
Listing 14-13

1 Vary: Accept-Encoding, User-Agent

Cet entte Vary particulier permettra la mise en cache de versions diffrentes de la mme ressource
en se basant sur l'URI et la valeur des enttes Accept-Encoding et User-Agent.

L'objet Response propose une interface pour grer l'entte Vary:


Listing 14-14

1
2
3
4
5

// dfinit un entte "vary"


$response->setVary('Accept-Encoding');
// dfinit plusieurs enttes "vary"
$response->setVary(array('Accept-Encoding', 'User-Agent'));

La mthode setVary() prend un nom d'entte ou un tableau de noms d'entte pour lesquels la rponse
varie.

Expiration et Validation
Il est, bien entendu, possible d'utiliser la fois le modle de validation et d'expiration pour un mme
objet Response. Mais comme le modle d'expiration l'emporte sur le modle de validation, il est facile
de bnficier du meilleur des deux modles. En d'autres termes en utilisant la fois l'expiration et la
validation, vous pouvez programmer le cache pour qu'il fournisse son contenu pendant qu'il vrifie
intervalle rgulier (l'expiration) que ce contenu est toujours valide.

14. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html#setNotModified()

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 208

Les autres mthodes de l'objet Response


La classe Response fournit beaucoup d'autres mthodes en relation avec la gestion du cache. Voici les
plus utiles:
Listing 14-15

1
2
3
4
5

// Marque l'objet Response comme obsolte


$response->expire();
// Forcer le retour d'une rponse 304 sans aucun contenu
$response->setNotModified();

La plupart des enttes en relation avec la gestion du cache peuvent tre dfinis avec la seule mthode
setCache()15:
Listing 14-16

1 // Dfinit la configuration du cache en un seul appel


2 $response->setCache(array(
3
'etag'
=> $etag,
4
'last_modified' => $date,
5
'max_age'
=> 10,
6
's_maxage'
=> 10,
7
'public'
=> true,
8
// 'private'
=> true,
9 ));

Utilisation de la technologie Edge Side Includes


Les passerelles de caches sont une bonne solution pour amliorer les performances d'un site. Mais elles
ont une limitation : elles peuvent uniquement mettre en cache une page dans son intgralit. S'il n'est pas
possible de mettre une page entire en cache ou si des parties de cette page sont plus dynamiques que
d'autres, cela pose problme. Heureusement, Symfony2 fournit une solution pour ces situations, base
sur la technologie Edge Side Includes , aussi appele ESI16. Akama a crit cette spcification il y a 10
ans ; elle permet de mettre en cache une partie de page avec une stratgie diffrente de l'ensemble de la
page.
La spcification ESI dcrit des marqueurs ( tags ) qui peuvent tre embarqus dans la page pour
communiquer avec la passerelle de cache. Un seul marqueur est implment dans Symfony2, include
car c'est le seul qui est utile en dehors du contexte Akama :
Listing 14-17

1 <!DOCTYPE html>
2 <html>
3
<body>
4
<!-- ... du contenu -->
5
6
<!-- inclut le contenu d'une autre page ici -->
7
<esi:include src="http://..." />
8
9
<!-- ... du contenu -->
10
</body>
11 </html>

15. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html#setCache()
16. http://www.w3.org/TR/esi-lang

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 209

L'exemple montre que chaque marqueur ESI a une URL complte (fully-qualified). Un marqueur
ESI reprsente un morceau de page qui peut tre appel via une URL donne.

Quand une requte est envoye, la passerelle de cache appelle la page entire depuis son espace de
stockage ou depuis le backend de l'application. Si la rponse contient un ou plusieurs marqueurs
ESI, ils sont grs de la mme manire. En d'autres termes, la passerelle de cache rcupre les fragments
de page de son cache, ou demande l'application de les recalculer. Quand tous les marqueurs ont t
calculs, la passerelle les fusionne avec la page principale et envoie le contenu final vers le client.
Le processus est gr de manire transparente au niveau de la passerelle de cache (c-a-d l'extrieur de
l'application). Comme vous pouvez le voir, si vous dcidez de prendre l'avantage des marqueurs ESI,
Symfony2 ralise le procd pour les inclure presque sans effort.

Utiliser ESI avec Symfony2


Premirement, pour utiliser ESI, il faut l'activer dans la configuration de l'application :
Listing 14-18

1 # app/config/config.yml
2 framework:
3
# ...
4
esi: { enabled: true }

Maintenant, prenons l'exemple d'une page statique, except pour l'espace Actualits qui se trouve en
bas de page. Avec ESI, il est possible de mettre en cache la partie qui gre les actualits indpendamment
du reste de la page.
Listing 14-19

1 public function indexAction()


2 {
3
$response = $this->render('MyBundle:MyController:index.html.twig');
4
// dfinit l'ge maximal partag - cela marque aussi la rponse comme tant publique
5
$response->setSharedMaxAge(600);
6
7
return $response;
8 }

Dans cet exemple, la page a une esprance de vie de 10 minutes en cache. Dans un deuxime temps,
incluons l'lment relatif l'actualit dans un template via une action embarque. Ceci sera ralis grce
au helper render (voir la documentation sur Contrleurs imbriqus pour plus de dtails).
Comme le contenu embarqu provient d'une autre page (ou d'un autre contrleur), Symfony2 utilise le
helper standard render pour configurer le marqueur ESI :
Listing 14-20

1
2
3
4
5

{# vous pouvez utiliser une rfrence de contrleur #}


{{ render_esi(controller('...:news', { 'max': 5 })) }}
{# ... ou une URL #}
{{ render_esi(url('latest_news', { 'max': 5 })) }}

En utilisant le rendu esi (via la fonction Twig render_esi), vous spcifiez Symfony que l'action doit
tre retourne dans une balise ESI. Vous vous demandez peut tre pourquoi il est prfrable d'utiliser un
helper plutt que d'afficher la balise ESI vous mme. C'est parce qu'en utilisant un helper, vous tes sur
que votre application fonctionnera mme si aucune passerelle de cache n'est installe.
Lorsque vous utilisez la fonction render (ou en dfinissant le rendu inline), Symfony2 merge le
contenu de la page incluse dans la page principale avant d'envoyer la rponse au client. Mais si vous

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 210

utilisez le rendu esi (c-a-d en appelant render_esi), et si Symfony2 dtecte une passerelle de cache qui
supporte ESI, alors une balise include ESI est gnre. Mais s'il y a aucune passerelle de cache, ou si elle
ne supporte pas ESI, Symfony2 mergera le contenu de la page incluse dans la page principale, comme si
vous aviez appel render.
Symfony2 dtecte si la passerelle gre les marqueurs ESI grce une autre spcification de Akama
qui est d'ores et dj support par le reverse proxy de Symfony2.

L'action incluse peut maintenant spcifier ses propres rgles de gestion du cache, entirement
indpendamment du reste de la page.
Listing 14-21

1 public function newsAction($max)


2 {
3
// ...
4
5
$response->setSharedMaxAge(60);
6 }

Avec ESI, la page complte sera valide pendant 600 secondes, mais le composant de gestion des actualits
ne le sera que pendant 60 secondes.
Lorsque vous utilisez une rfrence de contrleur, le tag ESI doit pouvoir appeler l'action incluse via une
URL accessible pour que la passerelle de cache puisse la recharger indpendamment du reste de la page.
Symfony2 se charge de gnrer une URL unique pour chaque rfrence de contrleur et est capable de les
router correctement grce un couteur qui doit tre activ dans votre configuration:
Listing 14-22

1 # app/config/config.yml
2 framework:
3
# ...
4
fragments: { path: /_fragment }

Un des grands avantages de cette stratgie de cache est qu'il est possible d'avoir une application aussi
dynamique que souhait tout en faisant appel cette application le moins possible.
L'couteur ne rpond qu' des adresses IP locale ou a des proxys de confiance.

Une fois que ESI est utilise, il ne faut pas oublier de toujours utiliser la directive s-maxage la
place de max-age. Comme le navigateur ne reoit que la rponse agrge de la ressource, il n'est
pas conscient de son sous-contenu , il suit la directive max-age et met toute la page en cache. Et
ce n'est pas ce que vous voulez.

Le helper render_esi supporte deux autres options utiles :


alt: utilise comme l'attribut alt du marqueur ESI, il permet de spcifier une URL alternative
si la ressource src ne peut pas tre trouve ;
ignore_errors: s'il est dfini true, un attribut onerror sera ajout l'ESI avec une valeur
continue indiquant que, en cas d'chec, la passerelle de cache enlvera le marqueur ESI sans
erreur ou warning.

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 211

Invalidation du cache
There are only two hard things in Computer Science: cache invalidation and naming things.
--Phil Karlton
Ceci peut tre traduit comme : Il existe uniquement deux oprations dlicates en Informatique :
l'invalidation de cache et nommer les choses.
Vous ne devriez jamais avoir besoin d'invalider des donnes du cache parce que l'invalidation est dj
prise en compte nativement par le modle de gestion du cache HTTP. Si la validation est utilise, vous ne
devriez pas avoir besoin d'utiliser l'invalidation par dfinition ; si l'expiration est utilise et que vous avez
besoin d'invalider une ressource, c'est que la date d'expiration a t dfinie trop loin dans le futur.
Puisque l'invalidation est un sujet spcifique chaque type de reverse proxy, si vous ne vous
occupez pas de l'invalidation, vous pouvez passer d'un reverse proxy l'autre sans changer quoi
que ce soit au code de votre application.

En fait, tous les reverse proxies fournissent un moyen de purger les donnes du cache mais il faut
l'viter autant que possible. Le moyen le plus standard est de purger le cache pour une URL donne en
l'appelant avec la mthode HTTP spciale PURGE.
Voici comment configurer le reverse proxy de Symfony2 pour supporter la mthode HTTP PURGE:
Listing 14-23

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

// app/AppCache.php
// ...
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class AppCache extends HttpCache
{
protected function invalidate(Request $request, $catch = false)
{
if ('PURGE' !== $request->getMethod()) {
return parent::invalidate($request, $catch);
}
$response = new Response();
if (!$this->getStore()->purge($request->getUri())) {
$response->setStatusCode(404, 'Not purged');
} else {
$response->setStatusCode(200, 'Purged');
}
return $response;
}
}

New in version 2.4: Le support des constantes de code statut HTTP a t ajout dans Symfony 2.4.

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 212

Il faut protger cette mthode HTTP PURGE d'une manire ou d'une autre pour viter que n'importe
qui ne puisse purger le cache.

Rsum
Symfony2 a t conu pour suivre les rgles prouves du protocole HTTP. La mise en cache n'y fait
pas exception. Comprendre le systme de cache de Symfony2 revient bien comprendre les modles
de gestion du cache HTTP et les utiliser efficacement. Ceci veut dire qu'au lieu de vous appuyer
uniquement sur la documentation et les exemples de code de Symfony2, vous pouvez vous ouvrir un
monde plein de connaissances relatives au cache et passerelles de cache HTTP telles que Varnish.

En savoir plus grce au Cookbook


Comment utiliser Varnish pour acclrer mon site Web

PDF brought to you by


generated on January 14, 2015

Chapter 14: Le Cache HTTP | 213

Chapter 15

Traductions
Le terme internationalisation (souvent abrg i18n1) dsigne le processus d'abstraction des textes et
autres spcificits locales en dehors de votre application qui sont ensuite placs dans un fichier o ils
peuvent tre traduits et convertis en se basant sur la locale de l'utilisateur (i.e. la langue et le pays). Pour
du texte, cela signifie l'encadrer avec une fonction capable de traduire le texte (ou message ) dans la
langue de l'utilisateur :
Listing 15-1

1
2
3
4
5

// le texte va *toujours* s'afficher en anglais


echo 'Hello World';
// le texte peut tre traduit dans la langue de l'utilisateur final ou par dfaut en anglais
echo $translator->trans('Hello World');

Le terme locale dsigne en gros la langue et le pays de l'utilisateur. Cela peut tre n'importe quelle
chane de caractres que votre application va utiliser pour grer les traductions et autres diffrences
de format (par ex. format de monnaie). Le code langue ISO639-12, un underscore (_), et ensuite
le code pays ISO3166 Alpha-23 (par ex. fr_FR pour Franais/France) est recommand.

Dans ce chapitre, vous apprendrez comment prparer une application grer de multiples locales et
ensuite comment crer des traductions pour plusieurs locales. Dans l'ensemble, le processus a plusieurs
tapes communes :
1. Activer et configurer le composant Translation de Symfony ;
2. Faire abstraction des chanes de caractres (i.e. messages ) en les encadrant avec des appels
au Translator ;
3. Crer des ressources de traduction pour chaque locale supporte qui traduit chaque message
dans l'application ;
4. Dterminer, dfinir et grer la locale de l'utilisateur dans la requte, et optionnellement dans la
session.

1. http://fr.wikipedia.org/wiki/Internationalisation_et_localisation
2. http://fr.wikipedia.org/wiki/Liste_des_codes_ISO_639-1
3. http://fr.wikipedia.org/wiki/ISO_3166-1#Table_de_codage

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 214

Configuration
Les traductions sont traites par le service Translator qui utilise la locale de l'utilisateur pour chercher
et retourner les messages traduits. Avant de l'utiliser, activez le Translator dans votre configuration :
Listing 15-2

1 # app/config/config.yml
2 framework:
3
translator: { fallback: en }

L'option fallback dfinit la locale de secours utiliser quand une traduction n'existe pas dans la locale
de l'utilisateur.
Quand une traduction n'existe pas pour une locale donne, le traducteur ( Translator ) essaye
tout d'abord de trouver une traduction pour cette langue (fr si la locale est fr_FR par exemple). Si
cela choue galement, il regarde alors pour une traduction utilisant la locale de secours.

La locale utilise pour les traductions est celle qui est stocke dans la requte. Vous pouvez la dfinir
grce l'attribut _locale de vos routes (La locale et l'URL).

Traduction basique
La traduction du texte est faite travers le service translator (Translator4). Pour traduire un bloc de
texte (appel un message), utilisez la mthode trans()5. Supposons, par exemple, que vous traduisez un
simple message dans un contrleur :
Listing 15-3

1 public function indexAction()


2 {
3
$t = $this->get('translator')->trans('Symfony2 is great');
4
5
return new Response($t);
6 }

Quand ce code est excut, Symfony2 va essayer de traduire le message Symfony2 is great en se basant
sur la locale de l'utilisateur. Pour que cela marche, vous devez dire Symfony2 comment traduire le
message via une ressource de traduction , qui est une collection de traductions de messages pour une
locale donne. Ce dictionnaire de traduction peut tre cr en plusieurs formats diffrents, XLIFF
tant le format recommand :
Listing 15-4

1
2
3
4
5
6
7
8
9
10
11
12

<!-- messages.fr.xliff -->


<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>J'aime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>

4. http://api.symfony.com/2.6/Symfony/Component/Translation/Translator.html
5. http://api.symfony.com/2.6/Symfony/Component/Translation/Translator.html#trans()

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 215

Maintenant, si la langue de la locale de l'utilisateur est le franais, (par ex. fr_FR ou fr_BE), le message
va tre traduit par J'aime Symfony2.

Le processus de traduction
Pour traduire le message, Symfony2 utilise un processus simple :
La locale de l'utilisateur actuel, qui est stocke dans la requte (ou stocke comme _locale
en session), est dtermine ;
Un catalogue des messages traduits est charg depuis les ressources de traduction dfinies pour
la locale (par ex. fr_FR). Les messages de la locale de secours sont aussi chargs et ajouts au
catalogue s'ils n'existent pas dj. Le rsultat final est un large dictionnaire de traductions.
Voir Catalogues de Message pour plus de dtails ;
Si le message est dans le catalogue, la traduction est retourne. Sinon, le traducteur retourne le
message original.
Lorsque vous utilisez la mthode trans(), Symfony2 cherche la chane de caractres exacte l'intrieur
du catalogue de messages appropri et la retourne (si elle existe).

Message avec paramtres de substitution


Parfois, un message contenant une variable a besoin d'tre traduit :
Listing 15-5

1 public function indexAction($name)


2 {
3
$t = $this->get('translator')->trans('Hello '.$name);
4
5
return new Response($t);
6 }

Cependant, crer une traduction pour cette chane de caractres est impossible tant donn que le
traducteur va essayer de trouver le message exact, incluant les portions de la variable (par ex. Hello
Ryan ou Hello Fabien ). Au lieu d'crire une traduction pour toutes les itrations possibles de la
variable $name, vous pouvez remplacer la variable avec un paramtre de substitution ( placeholder ) :
Listing 15-6

1 public function indexAction($name)


2 {
3
$t = $this->get('translator')->trans('Hello %name%', array('%name%' => $name));
4
5
new Response($t);
6 }

Symfony2 va maintenant chercher une traduction du message brut (Hello %name%) et ensuite remplacer
les paramtres de substitution avec leurs valeurs. Crer une traduction se fait comme prcdemment :
Listing 15-7

1 <!-- messages.fr.xliff -->


2 <?xml version="1.0"?>
3 <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
4
<file source-language="en" datatype="plaintext" original="file.ext">
5
<body>
6
<trans-unit id="1">
7
<source>Hello %name%</source>
8
<target>Bonjour %name%</target>
9
</trans-unit>
10
</body>

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 216

11
</file>
12 </xliff>

Les paramtres de substitution peuvent prendre n'importe quelle forme puisque le message en
entier est reconstruit en utilisant la fonction strtr6 de PHP . Cependant, la notation %var% est
requise pour les traductions dans les templates Twig, et c'est une convention gnrale suivre.

Comme vous l'avez vu, crer une traduction est un processus en deux tapes :
1. Faire abstraction du message qui a besoin d'tre traduit en le passant travers le Translator.
2. Crer une traduction pour le message dans chaque locale que vous avez choisi de supporter.
La deuxime tape est faite en crant des catalogues de messages qui dfinissent les traductions pour
chacune des diffrentes locales.

Catalogues de Message
Lorsqu'un message est traduit, Symfony2 compile un catalogue de messages pour la locale de l'utilisateur
et cherche dedans une traduction du message. Un catalogue de messages est comme un dictionnaire de
traductions pour une locale spcifique. Par exemple, le catalogue de la locale fr_FR pourrait contenir la
traduction suivante :
Symfony2 is Great => J'aime Symfony2
C'est la responsabilit du dveloppeur (ou du traducteur) d'une application internationalise de crer ces
traductions. Les traductions sont stockes sur le systme de fichiers et reconnues par Symfony, grce
certaines conventions.
Chaque fois que vous crez une nouvelle ressource de traduction (ou installez un bundle qui
comprend une ressource de traduction), assurez-vous de vider votre cache afin que Symfony puisse
reconnatre les nouvelles traductions :
Listing 15-8

1 $ php app/console cache:clear

Emplacements des Traductions et Conventions de Nommage


Symfony2 cherche les fichiers de messages (c-a-d les traductions) aux endroits suivants :
Le rpertoire <rpertoire racine du noyau>/Resources/translations;
Le rpertoire <rpertoire racine du noyau>/Resources/<nom du bundle>/translations;
Le rpertoire Resources/translations/ du bundle.
Les rpertoires sont lists par ordre de priorit. Cela signifie que vous pouvez surcharger les messages de
traduction d'un bundle dans l'un des deux premiers rpertoires.
Le systme de surcharge se base sur les cls : seules les cls surcharges ont besoin d'tre listes dans un
fichier de plus grande priorit. Quand une cl n'est pas trouve dans un fichier de traductions, le service
Translator cherchera automatiquement dans les fichiers de moindre priorit.

6. http://www.php.net/manual/fr/function.strtr.php

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 217

Le nom des fichiers de traductions est aussi important puisque Symfony2 utilise une convention pour
dterminer les dtails propos des traductions. Chaque fichier de messages doit tre nomm selon le
schma suivant : domaine.locale.format :
domaine: Une faon optionnelle d'organiser les messages dans des groupes (par ex. admin,
navigation ou messages par dfaut) - voir Utiliser les Domaines de Message;
locale: La locale de la traduction (par ex. en_GB, en, etc);
format: Comment Symfony2 doit charger et analyser le fichier (par ex. xliff, php ou yml).
La valeur du format peut tre le nom de n'importe quel format enregistr. Par dfaut, Symfony fournit les
formats suivants :
xliff : fichier XLIFF ;
php : fichier PHP ;
yml : fichier YAML.
Le choix du format utiliser dpend de vous, c'est une question de got.
Vous pouvez galement stocker des traductions dans une base de donnes, ou tout autre systme
de stockage en fournissant une classe personnalise implmentant l'interface LoaderInterface7.

Crer les Traductions


Le fait de crer des fichiers de traduction est une partie importante de la localisation (souvent abrge
L10n8). Les fichiers de traduction consistent en une srie de paires id-traduction pour un domaine et une
locale donns. La source est l'identifiant de la traduction individuelle, et peut tre le message dans la
locale principale (par exemple Symfony is great ) de votre application ou un identificateur unique (par
exemple symfony2.great - voir l'encadr ci-dessous) :
Listing 15-9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<!-- src/Acme/DemoBundle/Resources/translations/messages.fr.xliff -->


<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>J'aime Symfony2</target>
</trans-unit>
<trans-unit id="2">
<source>symfony2.great</source>
<target>J'aime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>

Symfony2 va reconnatre ces fichiers et les utiliser lors de la traduction de Symfony2 is great ou de
symfony2.great dans une locale de langue franaise (par ex. fr_FR or fr_BE).

7. http://api.symfony.com/2.6/Symfony/Component/Translation/Loader/LoaderInterface.html
8. http://fr.wikipedia.org/wiki/Localisation_(informatique)

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 218

Utiliser des mots-cls ou des messages


Cet exemple illustre les deux philosophies diffrentes lors de la cration des messages traduire :
Listing 15-10

1 $t = $translator->trans('Symfony2 is great');
2
3 $t = $translator->trans('symfony2.great');

Dans la premire mthode, les messages sont crits dans la langue de la locale par dfaut (anglais
dans ce cas). Ce message est ensuite utilis comme id lors de la cration des traductions.
Dans la seconde mthode, les messages sont en fait des mots-cls qui voquent l'ide du
message. Le message mot-cl est ensuite utilis comme id pour toutes les traductions. Dans
ce cas, les traductions doivent (aussi) tre faites pour la locale par dfaut (i.e. pour traduire
symfony2.great en Symfony2 is great).
La deuxime mthode est trs pratique, car la cl du message n'aura pas besoin d'tre modifie
dans chaque fichier de traduction si vous dcidez que le message devrait en fait tre Symfony2 is
really great dans la locale par dfaut.
Le choix de la mthode utiliser dpend entirement de vous, mais le format mot-cl est
souvent recommand.
En outre, les formats de fichiers php et yaml prennent en charge les ids imbriqus pour viter de
vous rpter, si vous utilisez des mots-cls plutt que du texte brut comme id :
Listing 15-11

1 symfony2:
2
is:
3
great: Symfony2 is great
4
amazing: Symfony2 is amazing
5
has:
6
bundles: Symfony2 has bundles
7 user:
8
login: Login

Les multiples niveaux sont convertis en paires uniques id / traduction par l'ajout d'un point (.)
entre chaque niveau ; donc les exemples ci-dessus sont quivalents ce qui suit :
Listing 15-12

1
2
3
4

symfony2.is.great: Symfony2 is great


symfony2.is.amazing: Symfony2 is amazing
symfony2.has.bundles: Symfony2 has bundles
user.login: Login

Utiliser les Domaines de Message


Comme vous l'avez vu, les fichiers de messages sont organiss par les diffrentes locales qu'ils traduisent.
Pour plus de structure, les fichiers de messages peuvent galement tre organiss en domaines . Lors
de la cration des fichiers de messages, le domaine est la premire partie du nom du fichier. Le domaine
par dfaut est messages. Par exemple, supposons que, par souci d'organisation, les traductions ont t
divises en trois domaines diffrents : messages, admin et navigation. La traduction franaise aurait les
fichiers de message suivants :
messages.fr.xliff
admin.fr.xliff
navigation.fr.xliff

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 219

Lors de la traduction de chanes de caractres qui ne sont pas dans le domaine par dfaut (messages),
vous devez spcifier le domaine comme troisime argument de trans() :
Listing 15-13

1 $this->get('translator')->trans('Symfony2 is great', array(), 'admin');

Symfony2 va maintenant chercher le message dans le domaine admin de la locale de l'utilisateur.

Grer la Locale de l'Utilisateur


La locale de l'utilisateur courant est stocke dans la requte et est accessible via l'objet request :
Listing 15-14

1
2
3
4
5
6

// Accder l'objet Request dans un contrleur standard


$request = $this->getRequest();
$locale = $request->getLocale();
$request->setLocale('en_US');

Il est aussi possible de stocker la locale en session plutt qu'en requte. Si vous faites cela, toutes les
requtes auront la mme locale.
Listing 15-15

1 $this->get('session')->set('_locale', 'en_US');

Lisez le chapitre La locale et l'URL pour voir comment dfinir la locale dans vos routes.

Solution de Secours et Locale par Dfaut


Si la locale n'a pas t explicitement dfinie dans la session, le paramtre de configuration
fallback_locale va tre utilis par le Translator. Le paramtre est dfini comme en par dfaut (voir
Configuration).
Alternativement, vous pouvez garantir que la locale soit dfinie dans chaque requte de l'utilisateur en
dfinissant le paramtre default_locale du framework :
Listing 15-16

1 # app/config/config.yml
2 framework:
3
default_locale: en

New in version 2.1: Le paramtre default_locale tait la base dfini dans la cl session,
cependant cela a chang dans la version 2.1. C'est parce que la locale est maintenant dfinie dans
la requte et non plus dans la session

La locale et l'URL
Puisque vous pouvez stocker la locale de l'utilisateur dans la session, il peut tre tentant d'utiliser la
mme URL pour afficher une ressource dans de nombreuses langues diffrentes en se basant sur la
locale de l'utilisateur. Par exemple, http://www.example.com/contact pourrait afficher le contenu en
anglais pour un utilisateur, et en franais pour un autre utilisateur. Malheureusement, cela viole une rgle
fondamentale du Web qui dit qu'une URL particulire retourne la mme ressource indpendamment de
l'utilisateur. Pour enfoncer encore plus le clou, quelle version du contenu serait indexe par les moteurs
de recherche ?

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 220

Une meilleure politique est d'inclure la locale dans l'URL. Ceci est entirement pris en charge par le
systme de routage en utilisant le paramtre spcial _locale :
Listing 15-17

1 contact:
2
pattern:
/{_locale}/contact
3
defaults: { _controller: AcmeDemoBundle:Contact:index, _locale: en }
4
requirements:
5
_locale: en|fr|de

Lorsque vous utilisez le paramtre spcial _locale dans une route, la locale correspondante sera
automatiquement dfinie dans la session de l'utilisateur. En d'autres termes, si un utilisateur visite l'URI
/fr/contact, la locale fr sera automatiquement dfinie comme la locale de sa session.
Vous pouvez maintenant utiliser la locale de l'utilisateur pour crer des routes pointant vers d'autres
pages traduites de votre application.

Pluralisation
La pluralisation des messages est un sujet difficile, car les rgles peuvent tre assez complexes. Par
exemple, voici la reprsentation mathmatique des rgles de la pluralisation russe :
Listing 15-18

1 (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10


<= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);

Comme vous pouvez le voir, en russe, vous pouvez avoir trois diffrentes formes de pluriel, chacune
donnant un index de 0, 1 ou 2. Pour chaque forme, le pluriel est diffrent, et ainsi la traduction est
galement diffrente.
Quand une traduction a des formes diffrentes dues la pluralisation, vous pouvez fournir toutes les
formes comme une chane de caractres spare par un pipe (|):
Listing 15-19

1 'There is one apple|There are %count% apples'

Pour traduire des messages pluraliss, utilisez la mthode transChoice()9 :


Listing 15-20

1 $t = $this->get('translator')->transChoice(
2
'There is one apple|There are %count% apples',
3
10,
4
array('%count%' => 10)
5 );

Le second paramtre (10 dans cet exemple), est le nombre d'objets tant dcrits et est utilis pour
dterminer quelle traduction utiliser et aussi pour dfinir le paramtre de substitution %count%.
En se basant sur le nombre donn, le traducteur choisit la bonne forme du pluriel. En anglais, la plupart
des mots ont une forme singulire quand il y a exactement un objet et un pluriel pour tous les autres
nombres (0, 2, 3 ...). Ainsi, si count vaut 1, le traducteur va utiliser la premire chane de caractres
(There is one apple) comme traduction. Sinon, il va utiliser There are %count% apples.
Voici la traduction franaise :
Listing 15-21

1 'Il y a %count% pomme|Il y a %count% pommes'

9. http://api.symfony.com/2.6/Symfony/Component/Translation/Translator.html#transChoice()

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 221

Mme si la chane de caractres se ressemble (elle est constitue de deux sous-chanes spares par un
pipe), les rgles franaises sont diffrentes : la premire forme (pas de pluriel) est utilise lorsque count
vaut 0 ou 1. Ainsi, le traducteur va utiliser automatiquement la premire chane (Il y a %count% pomme)
lorsque count vaut 0 ou 1.
Chaque locale a son propre ensemble de rgles, certaines ayant jusqu' six diffrentes formes plurielles
avec des rgles complexes pour dterminer quel nombre correspond quelle forme du pluriel. Les rgles
sont assez simples pour l'anglais et le franais, mais pour le russe, vous auriez voulu un indice pour
savoir quelle rgle correspond quelle chane de caractres. Pour aider les traducteurs, vous pouvez
ventuellement taguer chaque chane :
Listing 15-22

1 'one: There is one apple|some: There are %count% apples'


2
3 'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes'

Les tags sont seulement des indices pour les traducteurs et n'affectent pas la logique utilise pour
dterminer quelle forme de pluriel utiliser. Les tags peuvent tre toute chane descriptive qui se termine
par un deux-points (:). Les tags n'ont pas besoin d'tre les mmes dans le message original que dans la
traduction.
Comme les tags sont optionnels, le traducteur ne les utilise pas (il va seulement obtenir une chane
de caractres en fonction de sa position dans la chane).

Intervalle Explicite de Pluralisation


La meilleure faon de pluraliser un message est de laisser Symfony2 utiliser sa logique interne pour choisir
quelle chane utiliser en se basant sur un nombre donn. Parfois, vous aurez besoin de plus de contrle
ou vous voudrez une traduction diffrente pour des cas spcifiques (pour 0, ou lorsque le nombre est
ngatif, par exemple). Pour de tels cas, vous pouvez utiliser des intervalles mathmatiques explicites :
Listing 15-23

1 '{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf]
There are many apples'

Les intervalles suivent la notation ISO 31-1110 . La chane de caractres ci-dessus spcifie quatre
intervalles diffrents : exactement 0, exactement 1, 2-19, et 20 et plus.
Vous pouvez galement mlanger les rgles mathmatiques explicites et les rgles standards. Dans ce cas,
si le nombre ne correspond pas un intervalle spcifique, les rgles standards prennent effet aprs la
suppression des rgles explicites :
Listing 15-24

1 '{0} There are no apples|[20,Inf] There are many apples|There is one apple|a_few: There are
%count% apples'

Par exemple, pour 1 pomme ( apple ), la rgle standard There is one apple va tre utilise. Pour
2-19 pommes ( apples ), la seconde rgle standard There are %count% apples va tre slectionne.
Une classe Interval11 peut reprsenter un ensemble fini de nombres :
Listing 15-25

1 {1,2,3,4}

Ou des nombres entre deux autres nombres :


10. http://en.wikipedia.org/wiki/ISO_31-11
11. http://api.symfony.com/2.6/Symfony/Component/Translation/Interval.html

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 222

Listing 15-26

1 [1, +Inf[
2 ]-1,2[

Le dlimiteur gauche peut-tre [ (inclusif) ou ] (exclusif). Le delimiteur droit peut-tre [ (exclusif) ou ]


(inclusif). En sus des nombres, vous pouvez utiliser -Inf and +Inf pour l'infini.

Traductions dans les Templates


La plupart du temps, les traductions surviennent dans les templates. Symfony2 supporte nativement les
deux types de templates que sont Twig et PHP.

Templates Twig
Symfony2 fournit des balises Twig spcialises (trans et transchoice) pour aider la traduction des
blocs statiques de texte :
Listing 15-27

1 {% trans %}Hello %name%{% endtrans %}


2
3 {% transchoice count %}
4
{0} There is no apples|{1} There is one apple|]1,Inf] There are %count% apples
5 {% endtranschoice %}

La balise transchoice prend automatiquement la variable %count% depuis le contexte actuel et la passe
au traducteur. Ce mcanisme fonctionne seulement lorsque vous utilisez un paramtre de substitution
suivi du pattern %var%.
Si vous avez besoin d'utiliser le caractre pourcentage (%) dans une chane de caractres, chappezle en le doublant : {% trans %}Percent: %percent%%%{% endtrans %}

Vous pouvez galement spcifier le domaine de messages et passer quelques variables supplmentaires :
Listing 15-28

1
2
3
4
5
6
7

{% trans with {'%name%': 'Fabien'} from "app" %}Hello %name%{% endtrans %}


{% trans with {'%name%': 'Fabien'} from "app" into "fr" %}Hello %name%{% endtrans %}
{% transchoice count with {'%name%': 'Fabien'} from "app" %}
{0} There is no apples|{1} There is one apple|]1,Inf] There are %count% apples
{% endtranschoice %}

Les filtres trans et transchoice peuvent tre utiliss pour traduire les textes de variable ainsi que les
expressions complexes :
Listing 15-29

1
2
3
4
5
6
7

{{ message|trans }}
{{ message|transchoice(5) }}
{{ message|trans({'%name%': 'Fabien'}, "app") }}
{{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }}

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 223

Utiliser les balises ou filtres de traduction a le mme effet, mais avec une diffrence subtile :
l'chappement automatique en sortie est appliqu uniquement aux variables traduites via un filtre.
En d'autres termes, si vous avez besoin d'tre sr que votre variable traduite n'est pas chappe en
sortie, vous devez appliquer le filtre brut aprs le filtre de traduction :
Listing 15-30

1
2
3
4
5
6
7
8
9
10
11
12

{# le texte traduit entre les balises n'est jamais chapp #}


{% trans %}
<h3>foo</h3>
{% endtrans %}
{% set message = '<h3>foo</h3>' %}

{# une variable traduite via un filtre est chappe par dfaut #}


{{ message|trans|raw }}
{# mais les chanes de caractres statiques ne sont jamais chappes #}
{{ '<h3>foo</h3>'|trans }}

New in version 2.1: Vous pouvez maintenant dfinir un domaine de traduction pour un template
Twig entier avec une seule balise :

Listing 15-31

1 {% trans_default_domain "app" %}

Notez que cela n'affecte que le template courant, pas les templates inclus pour viter les effets de bord.

Templates PHP
Le service de traduction est accessible dans les templates PHP travers l'outil d'aide translator :
Listing 15-32

1 <?php echo $view['translator']->trans('Symfony2 is great') ?>


2
3 <?php echo $view['translator']->transChoice(
4
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
5
10,
6
array('%count%' => 10)
7 ) ?>

Forcer la Locale du Traducteur


Lors de la traduction d'un message, Symfony2 utilise la locale de la requte courante ou la locale de
secours ( fallback locale ) si ncessaire. Vous pouvez galement spcifier manuellement la locale
utiliser pour la traduction :
Listing 15-33

1 $this->get('translator')->trans(
2
'Symfony2 is great',
3
array(),

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 224

4
'messages',
5
'fr_FR'
6 );
7
8 $this->get('translator')->transChoice(
9
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
10
10,
11
array('%count%' => 10),
12
'messages',
13
'fr_FR'
14 );

Traduire le Contenu d'une Base de Donnes


La traduction du contenu de bases de donnes devrait tre traite par Doctrine grce l'extension
Translatable Extension12. Pour plus d'informations, voir la documentation pour cette bibliothque.

Traduire les messages de contraintes


La meilleure manire de comprendre les contraintes de traduction est de les voir en action. Pour
commencer, supposez que vous avez cr un objet PHP que vous avez besoin d'utiliser quelque part dans
votre application :
Listing 15-34

1
2
3
4
5
6
7

// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}

Ajoutez des contraintes avec l'une des mthodes supportes. Dfinissez le texte source traduire dans
l'option message. Par exemple, pour garantir qu'une proprit $name n'est pas vide, ajoutez le code
suivant :
Listing 15-35

1 # src/Acme/BlogBundle/Resources/config/validation.yml
2 Acme\BlogBundle\Entity\Author:
3
properties:
4
name:
5
- NotBlank: { message: "author.name.not_blank" }

Crez un fichier de traduction pour le catalogue validators pour les messages de contraintes,
typiquement dans le rpertoire Resources/translations/ du bundle. Lisez Catalogues de Message pour
en savoir plus
Listing 15-36

1 <!-- validators.en.xliff -->


2 <?xml version="1.0"?>
3 <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
4
<file source-language="en" datatype="plaintext" original="file.ext">
5
<body>

12. https://github.com/l3pp4rd/DoctrineExtensions

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 225

6
<trans-unit id="1">
7
<source>author.name.not_blank</source>
8
<target>Saisissez un nom</target>
9
</trans-unit>
10
</body>
11
</file>
12 </xliff>

Rsum
Avec le composant Traduction de Symfony2, crer une application internationalise n'a plus besoin d'tre
un processus douloureux et se rsume simplement quelques tapes basiques :
Extraire les messages dans votre application en entourant chacun d'entre eux par la mthode
trans()13 ou par la mthode transChoice()14;
Traduire chaque message dans de multiples locales en crant des fichiers de message de
traduction. Symfony2 dcouvre et traite chaque fichier grce leur nom qui suit une
convention spcifique ;
Grer la locale de l'utilisateur, qui est stocke dans la requte, ou une fois pour toutes en
session.

13. http://api.symfony.com/2.6/Symfony/Component/Translation/Translator.html#trans()
14. http://api.symfony.com/2.6/Symfony/Component/Translation/Translator.html#transChoice()

PDF brought to you by


generated on January 14, 2015

Chapter 15: Traductions | 226

Chapter 16

Service Container
Une application PHP moderne est pleine d'objets. Un objet peut faciliter l'envoi des messages e-mail,
tandis qu'un autre peut vous permettre de persister les informations dans une base de donnes. Dans
votre application, vous pouvez crer un objet qui gre votre inventaire de produits, ou tout autre objet
qui traite des donnes via une API tierce. Le fait est qu'une application moderne fait beaucoup de choses
et est organise entre de nombreux objets qui grent chaque tche.
Ce chapitre traite d'un objet PHP spcial dans Symfony2 qui vous aide instancier, organiser et rcuprer
les nombreux objets de votre application. Cet objet, appel un conteneur de services, vous permettra de
standardiser et centraliser la faon dont les objets sont construits dans votre application. Le conteneur
vous facilite la vie, est super rapide, et met en valeur une architecture qui encourage un code rutilisable
et dcoupl. Puisque toutes les classes fondamentales de Symfony2 utilisent le conteneur, vous allez
apprendre comment tendre, configurer et utiliser n'importe quel objet dans Symfony2. En bien des
aspects, le conteneur de services est le principal responsable de la vitesse et de l'extensibilit de Symfony2.
Enfin, configurer et utiliser le conteneur de services est facile. A la fin de ce chapitre, vous serez l'aise
pour crer vos propres objets via le conteneur et pour personnaliser des objets provenant de bundles tiers.
Vous allez commencer crire du code qui est plus rutilisable, testable et dcoupl, tout simplement
parce que le conteneur de services facilite l'criture de code de qualit.
Si vous souhaitez aller plus loin aprs avoir lu ce chapitre, jetez un coup d'oeil la documentation
du composant Injection de Dpendance composant Injection Dpendance.

Qu'est-ce qu'un Service ?


Dit simplement, un Service dsigne tout objet PHP qui effectue une sorte de tche globale . C'est
un nom gnrique utilis en informatique pour dcrire un objet qui est cr dans un but prcis (par
ex. l'envoi des emails). Chaque service est utilis tout au long de votre application lorsque vous avez
besoin de la fonctionnalit spcifique qu'il fournit. Vous n'avez pas faire quelque chose de spcial pour
fabriquer un service : il suffit d'crire une classe PHP avec un code qui accomplit une tche spcifique.
Flicitations, vous venez tout juste de crer un service !

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 227

En rgle gnrale, un objet PHP est un service s'il est utilis de faon globale dans votre application.
Un seul service Mailer est utilis globalement pour envoyer des messages email tandis que les
nombreux objets Message qu'il dlivre ne sont pas des services. De mme, un objet Product n'est
pas un service, mais un objet qui persiste des objets Product dans une base de donnes est un
service.

Alors quel est l'avantage ? L'avantage de rflchir sur les services est que vous commencez penser
sparer chaque morceau de fonctionnalit dans votre application dans une srie de services. Puisque
chaque service ne ralise qu'une fonction, vous pouvez facilement accder chaque service et utiliser ses
fonctionnalits chaque fois que vous en avez besoin. Chaque service peut galement tre plus facilement
test et configur puisqu'il est spar des autres fonctionnalits de votre application. Cette ide est
appele service-oriented architecture1 et n'est pas unique Symfony2 ou encore PHP. Structurer votre
application autour d'un ensemble de classes de services indpendants est une bonne pratique oriente
objet clbre et fiable. Ces comptences sont les cls pour devenir un bon dveloppeur dans presque tous
les langages.

Dfinition d'un Conteneur de Services


Un Conteneur de services ( service container ou dependency injection container en anglais) est
simplement un objet PHP qui gre l'instanciation des services (c-a-d objets).
Par exemple, supposons que vous ayez une simple classe PHP qui envoie des messages email. Sans un
conteneur de services, vous devez manuellement crer l'objet chaque fois que vous en avez besoin :
Listing 16-1

1 use Acme\HelloBundle\Mailer;
2
3 $mailer = new Mailer('sendmail');
4 $mailer->send('ryan@foobar.net', ... );

Ceci est assez facile. La classe imaginaire Mailer vous permet de configurer la mthode utilise pour
envoyer les messages par e-mail (par exemple sendmail, smtp, etc) Mais que faire si vous voulez utiliser
le service mailer ailleurs ? Vous ne voulez certainement pas rpter la configuration du mailer chaque fois
que vous devez utiliser l'objet Mailer. Que se passe-t-il si vous avez besoin de changer le transport de
sendmail smtp partout dans l'application ? Vous auriez besoin de chercher chaque endroit o vous avez
cr un service Mailer et de le changer.

Crer/Configurer les services dans le Conteneur


Une meilleure solution est de laisser le conteneur de services crer l'objet Mailer pour vous. Pour que cela
fonctionne, vous devez spcifier au conteneur comment crer le Mailer. Cela se fait via la configuration,
qui peut tre spcifie en YAML, XML ou PHP :
Listing 16-2

1 # app/config/config.yml
2 services:
3
my_mailer:
4
class:
Acme\HelloBundle\Mailer
5
arguments:
[sendmail]

1. http://wikipedia.org/wiki/Service-oriented_architecture

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 228

Lorsque Symfony2 s'initialise, il construit le conteneur de services en utilisant la configuration


de l'application (app/config/config.yml par dfaut). Le fichier exact qui est charg est dict
par la mthode AppKernel::registerContainerConfiguration(), qui charge un fichier de
configuration spcifique l'environnement (par exemple config_dev.yml pour l'environnement
de dev ou config_prod.yml pour la prod).

Une instance de l'objet Acme\HelloBundle\Mailer est maintenant disponible via le conteneur de


services. Le conteneur est disponible dans tous les contrleurs traditionnels de Symfony2 o vous pouvez
accder aux services du conteneur via la mthode de raccourci get() :
Listing 16-3

1 class HelloController extends Controller


2 {
3
// ...
4
5
public function sendEmailAction()
6
{
7
// ...
8
$mailer = $this->get('my_mailer');
9
$mailer->send('ryan@foobar.net', ... );
10
}
11 }

Lorsque vous demandez le service my_mailer du conteneur, le conteneur construit l'objet et le retourne.
Ceci est un autre avantage majeur d'utiliser le conteneur de services. A savoir, un service n'est jamais
construit avant qu'il ne soit ncessaire. Si vous dfinissez un service et ne l'utilisez jamais sur une
demande, le service n'est jamais cr. Cela permet d'conomiser la mmoire et d'augmenter la vitesse de
votre application. Cela signifie aussi qu'il y a trs peu ou pas d'impact de performance en dfinissant
beaucoup de services. Les services qui ne sont jamais utiliss ne sont jamais construits.
Comme bonus supplmentaire, le service Mailer est seulement cr une fois et la mme instance est
retourne chaque fois que vous demandez le service. Ceci est presque toujours le comportement dont
vous aurez besoin (c'est plus souple et plus puissant), mais vous apprendrez comment configurer un
service qui a de multiples instances dans l'article du Cookbook Comment travailler avec les champs
d'applications ( scopes en anglais) .
Dans cet exemple, le contrleur tend le contrleur de base de Symfony, ce qui vous donne accs
au conteneur de services lui-mme. Vous pouvez alors utiliser la mthode get pour localiser et
retrouver le service my_mailer depuis le conteneur de services. Vous pouvez galement dfinir vos
contrleurs en tant que Services. Il s'agit d'un sujet un peu plus avanc et non ncessaire, mais cela
vous permet d'injecter uniquement les services dont vous avez besoin dans votre contrleur.

Paramtres de Service
La cration de nouveaux services (c-a-d objets) via le conteneur est assez simple. Les paramtres rendent
les dfinitions de services plus organises et flexibles :
Listing 16-4

1 # app/config/config.yml
2 parameters:
3
my_mailer.class:
4
my_mailer.transport:
5
6 services:

PDF brought to you by


generated on January 14, 2015

Acme\HelloBundle\Mailer
sendmail

Chapter 16: Service Container | 229

7
8
9

my_mailer:
class:
arguments:

"%my_mailer.class%"
["%my_mailer.transport%"]

Le rsultat final est exactement le mme que prcdemment - la diffrence est seulement dans la manire
dont vous avez dfini le service. En entourant les chanes my_mailer.class et my_mailer.transport
par le signe pour cent (%), le conteneur sait qu'il faut chercher des paramtres avec ces noms. Quand le
conteneur est construit, il cherche la valeur de chaque paramtre et l'utilise dans la dfinition du service.
Si vous voulez utiliser une chane de caractres commenant par le signe @ comme valeur de
paramtre (i.e. un mot de passe trs scuris) dans un fichier YAML, vous devez l'chaper en
ajoutant un autre signe @ (cela ne s'applique qu'au format YAML) :
Listing 16-5

1 # app/config/parameters.yml
2 parameters:
3
# Cela sera analys comme la chane "@securepass"
4
mailer_password: "@@securepass"

Le signe pour cent au sein d'un paramtre ou d'un argument, et qui fait partie de la chane de
caractres, doit tre chapp par un autre signe pour cent :
Listing 16-6

<argument type="string">http://symfony.com/?foo=%%s&bar=%%d</argument>

Le but des paramtres est de fournir l'information dans les services. Bien sr, il n'y avait rien de mal
dfinir le service sans utiliser de paramtre. Les paramtres, cependant, ont plusieurs avantages :
la sparation et l'organisation de toutes les options de service sous une seule cl de
paramtres ;
les valeurs de paramtres peuvent tre utilises dans de multiples dfinitions de service ;
Lors de la cration d'un service dans un bundle (vous allez voir ceci sous peu), utiliser les
paramtres permet au service d'tre facilement personnalis dans votre application.
Le choix d'utiliser ou non des paramtres dpend de vous. Les bundles tiers de haute qualit utiliseront
toujours les paramtres puisqu'ils rendent le service stock dans le conteneur plus configurable. Pour
les services dans votre application, cependant, vous pouvez ne pas avoir besoin de la flexibilit des
paramtres.

Tableaux de paramtres
Les paramtres ne sont pas obligatoirement des chanes de caractres, ils peuvent aussi tre des tableaux.
Pour le format XML, vous devez utiliser l'attribut type="collection" pour tous les paramtres qui sont des
tableaux.
Listing 16-7

1 # app/config/config.yml
2 parameters:
3
my_mailer.gateways:
4
- mail1
5
- mail2
6
- mail3
7
my_multilang.language_fallback:
8
en:

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 230

9
10
11
12
13

- en
- fr
fr:
- fr
- en

Importer d'autres Ressources de Configuration de Conteneur


Dans cette section, les fichiers de configuration de services font rfrence des ressources. C'est
pour souligner le fait que, alors que la plupart des ressources de configuration sont des fichiers (par
exemple YAML, XML, PHP), Symfony2 est si flexible que la configuration pourrait tre charge de
n'importe o (par exemple une base de donnes ou mme via un service web externe).

Le conteneur de services est construit en utilisant une ressource de configuration unique (app/config/
config.yml par dfaut). Toutes les autres configurations de service (y compris la configuration du noyau
de Symfony2 et des bundle tiers) doivent tre importes l'intrieur de ce fichier d'une manire ou d'une
autre. Cela vous donne une flexibilit absolue sur les services dans votre application.
La configuration des services externes peut tre importe de deux manires diffrentes. La premire, et
la plus commune, consiste utiliser la directive imports. Plus tard, vous apprendrez la seconde mthode
qui est la mthode flexible et prfre pour l'importation de configuration de services des bundles tiers.

Importer la Configuration avec imports


Jusqu'ici, vous avez plac notre dfinition de conteneur de service my_mailer directement dans le fichier
de configuration de l'application (par exemple app/config/config.yml). Bien sr, puisque la classe
Mailer elle-mme vit l'intrieur de AcmeHelloBundle, il est plus logique de mettre la dfinition du
conteneur my_mailer l'intrieur du bundle aussi.
Tout d'abord, dplacez la dfinition du conteneur my_mailer dans un nouveau fichier de ressource
de conteneur l'intrieur d' AcmeHelloBundle. Si les rpertoires Resources ou Resources/config
n'existent pas, crez-les.
Listing 16-8

1 # src/Acme/HelloBundle/Resources/config/services.yml
2 parameters:
3
my_mailer.class:
Acme\HelloBundle\Mailer
4
my_mailer.transport: sendmail
5
6 services:
7
my_mailer:
8
class:
"%my_mailer.class%"
9
arguments:
["%my_mailer.transport%"]

La dfinition elle-mme n'a pas chang, seulement son emplacement. Bien sr, le conteneur de service ne
connat pas le nouveau fichier de ressources. Heureusement, vous pouvez facilement importer le fichier
de ressources en utilisant la cl imports dans la configuration de l'application.
Listing 16-9

1 # app/config/config.yml
2 imports:
3
- { resource: "@AcmeHelloBundle/Resources/config/services.yml" }

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 231

La directive imports permet votre application d'inclure des ressources de configuration de conteneur
de services de n'importe quel autre emplacement (le plus souvent partir de bundles). L'emplacement
resource, pour les fichiers, est le chemin absolu du fichier de ressource. La syntaxe spciale @AcmeHello
rsout le chemin du rpertoire du bundle AcmeHelloBundle. Cela vous aide spcifier le chemin vers la
ressource sans se soucier plus tard, si vous dplacez le AcmeHelloBundle dans un autre rpertoire.

Importer la Configuration via les Extensions de Conteneur


Quand vous dvelopperez avec Symfony2, vous utiliserez le plus souvent la directive imports pour
importer la configuration du conteneur des bundles que vous avez cr spcifiquement pour votre
application. Les configurations des conteneurs des bundles tiers, y compris les services du noyau de
Symfony2, sont habituellement charges en utilisant une autre mthode qui est plus souple et facile
configurer dans votre application.
Voici comment cela fonctionne. En interne, chaque bundle dfinit ses services comme vous l'avez vu
jusqu' prsent. A savoir, un bundle utilise un ou plusieurs fichiers de ressources de configuration
(gnralement XML) pour spcifier les paramtres et les services pour ce bundle. Cependant, au lieu
d'importer chacune de ces ressources directement partir de la configuration de votre application en
utilisant la directive imports, vous pouvez simplement invoquer une extension du conteneur de services
l'intrieur du bundle qui fait le travail pour vous. Une extension de conteneur de services est une classe
PHP cre par l'auteur du bundle afin d'accomplir deux choses :
importer toutes les ressources du conteneur de services ncessaires pour configurer les services
pour le bundle ;
fournir une configuration smantique, simple de sorte que le bundle peut tre configur sans
interagir avec les paramtres de la configuration du conteneur de services du bundle.
En d'autres termes, une extension de conteneur de services configure les services pour un bundle en votre
nom. Et comme vous le verrez dans un instant, l'extension fournit une interface pratique, de haut niveau
pour configurer le bundle.
Prenez le FrameworkBundle - le bundle noyau du framework Symfony2 - comme un exemple. La prsence
du code suivant dans votre configuration de l'application invoque l'extension du conteneur de services
l'intrieur du FrameworkBundle :
Listing 16-10

1 # app/config/config.yml
2 framework:
3
secret:
xxxxxxxxxx
4
form:
true
5
csrf_protection: true
6
router:
{ resource: "%kernel.root_dir%/config/routing.yml" }
7
# ...

Lorsque la configuration est analyse, le conteneur cherche une extension qui peut grer la directive de
configuration du framework. L'extension en question, qui vit dans le FrameworkBundle, est invoque
et la configuration du service pour le FrameworkBundle est charge. Si vous retirez la cl framework
de votre fichier de configuration de l'application entirement, les services noyau de Symfony2 ne seront
pas chargs. Le fait est que vous avez la matrise : le framework Symfony2 ne contient pas de magie et
n'effectue aucune action dont vous n'avez pas le contrle dessus.
Bien sr, vous pouvez faire beaucoup plus que simplement activer l'extension du conteneur de services
du FrameworkBundle. Chaque extension vous permet de facilement personnaliser le bundle, sans vous
soucier de la manire dont les services internes sont dfinis.
Dans ce cas, l'extension vous permet de personnaliser le error_handler, csrf_protection, router
et bien plus encore. En interne, le FrameworkBundle utilise les options spcifies ici pour dfinir et
configurer les services qui lui sont spcifiques. Le bundle se charge de crer tous les paramtres
et services ncessaires pour le conteneur du service, tout en permettant une grande partie de la
PDF brought to you by
generated on January 14, 2015

Chapter 16: Service Container | 232

configuration d'tre facilement personnalise. Comme bonus supplmentaire, la plupart des extensions
du conteneur de services sont assez malines pour effectuer la validation - vous informant des options qui
sont manquantes ou du mauvais type de donnes.
Lors de l'installation ou la configuration d'un bundle, consultez la documentation du bundle pour savoir
comment installer et configurer les services pour le bundle. Les options disponibles pour les bundles du
noyau peuvent tre trouves Reference Guide.
Nativement, le conteneur de services reconnat seulement les directives parameters, services, et
imports. Toutes les autres directives sont gres par une extension du conteneur de service.

Si vous voulez exposer une configuration conviviale dans vos propres bundles, lisez l'entre du cookbook
"Comment exposer une configuration smantique pour un Bundle".

Refrencer (Injecter) les Services


Jusqu' prsent, notre service originel my_mailer est simple : il suffit d'un seul paramtre dans son
constructeur, qui est facilement configurable. Comme vous le verrez, la vraie puissance du conteneur est
dmontre lorsque vous avez besoin de crer un service qui dpend d'un ou plusieurs autres services dans
le conteneur.
Commenons par un exemple. Supposons que vous ayez un nouveau service, NewsletterManager
, qui aide grer la prparation et l'envoi d'un message email une liste d'adresses. Bien sr, le
service my_mailer excelle vraiment pour envoyer des messages email, donc vous allez l'utiliser dans
NewsletterManager pour grer l'envoi effectif des messages. Cette fausse classe pourrait ressembler
quelque chose comme ceci :
Listing 16-11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

namespace Acme\HelloBundle\Newsletter;
use Acme\HelloBundle\Mailer;
class NewsletterManager
{
protected $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}

// ...
}

Sans utiliser le conteneur de services, vous pouvez crer une nouvelle NewsletterManager assez
facilement l'intrieur d'un contrleur :
Listing 16-12

1 public function sendNewsletterAction()


2 {
3
$mailer = $this->get('my_mailer');
4
$newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer);
5
// ...
6 }

Cette approche est pas mal, mais si nous dcidons plus tard que la classe NewsletterManager a besoin
d'un deuxime ou troisime paramtre de constructeur ? Que se passe-t-il si nous dcidons de refactoriser
PDF brought to you by
generated on January 14, 2015

Chapter 16: Service Container | 233

notre code et de renommer la classe ? Dans les deux cas, vous auriez besoin de trouver tous les endroits
o le NewsletterManager a t instanci et de le modifier. Bien sr, le conteneur de services vous donne
une option beaucoup plus attrayante :
Listing 16-13

1 # src/Acme/HelloBundle/Resources/config/services.yml
2 parameters:
3
# ...
4
newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager
5
6 services:
7
my_mailer:
8
# ...
9
newsletter_manager:
10
class:
"%newsletter_manager.class%"
11
arguments: ["@my_mailer"]

En YAML, la syntaxe spciale @my_mailer indique au conteneur de chercher un service nomm


my_mailer et de transmettre cet objet dans le constructeur de NewsletterManager. Dans ce cas,
cependant, le service spcifi my_mailer doit exister. Si ce n'est pas le cas, une exception sera leve. Vous
pouvez marquer vos dpendances comme facultatives - nous en parlerons dans la section suivante.
Utiliser des rfrences est un outil trs puissant qui vous permet de crer des classes de services
indpendantes avec des dpendances bien dfinies. Dans cet exemple, le service newsletter_manager
a besoin du service my_mailer afin de fonctionner. Lorsque vous dfinissez cette dpendance dans le
conteneur de service, le conteneur prend soin de tout le travail de l'instanciation des objets.

Utiliser l'Expression Language


New in version 2.4: La fonctionnalit Expression Language a t intgr dans la version 2.4 de
Symfony.

Le conteneur de service supporte galement une "expression" permettant d'injecter une valeur trs
spcifique dans un service.
Par exemple, supposez que vous avez un service tiers (non prsent ici), appel mailer_configuration
ayant une mthode getMailerMethod(), cette mthode retournant une chane de caractres comme
sendmail bas sur une certaine configuration. Souvenez-vous que le premier argument du service
my_mailer est la simple chaine de caractre sendmail :
Listing 16-14

1 # app/config/config.yml
2 services:
3
my_mailer:
4
class:
Acme\HelloBundle\Mailer
5
arguments:
[sendmail]

Mais au lieu de coder cela en dur, comment pourrions-nous rcuprer cette valeur depuis
getMailerMethod() du nouveau service mailer_configuration ? Une des faon est d'utiliser une
expression :
Listing 16-15

1 # app/config/config.yml
2 services:
3
my_mailer:
4
class:
Acme\HelloBundle\Mailer
5
arguments:
["@=service('mailer_configuration').getMailerMethod()"]

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 234

Pour en apprendre plus sur l'expression language, jetez un coup d'oeil Syntaxe des expressions.
Dans ce context, vous avez accs deux fonctions :
service - retourne une service donn (confre l'exemple plus haut);
parameter - retourne une valeur de paramtre spcifique (la synthaxe est similaire service)
Vous avez galement accs la classe ContainerBuilder2 via la variable container. Voici un autre
exemple :
Listing 16-16

1 services:
2
my_mailer:
3
class:
Acme\HelloBundle\Mailer
4
arguments: ["@=container.hasParameter('some_param') ? parameter('some_param') :
'default_value'"]

Les expression peuvent tre utilises dans arguments, properties, comme arguments avec
configurator``et comme les arguments ``calls (mthode d'appels).

Dpendances optionnelles : Setter Injection


L'injection de dpendances dans le constructeur de cette manire est un excellent moyen de s'assurer que
la dpendance est disponible pour utilisation. Si vous avez des dpendances optionnelles pour une classe,
alors la mthode setter injection peut tre une meilleure option. Cela signifie d'injecter la dpendance
en utilisant un appel de mthode plutt que par le constructeur. La classe devrait ressembler ceci :
Listing 16-17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

namespace Acme\HelloBundle\Newsletter;
use Acme\HelloBundle\Mailer;
class NewsletterManager
{
protected $mailer;
public function setMailer(Mailer $mailer)
{
$this->mailer = $mailer;
}

// ...
}

L'injection de la dpendance par la mthode setter a juste besoin d'un changement de la syntaxe :
Listing 16-18

# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ...
newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager
services:
my_mailer:
# ...
newsletter_manager:
class:
"%newsletter_manager.class%"
calls:
- [ setMailer, [ @my_mailer ] ]

2. http://api.symfony.com/2.6/Symfony/Component/DependencyInjection/ContainerBuilder.html

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 235

Les approches prsentes dans cette section sont appeles constructor injection et setter
injection . Le conteneur de service Symfony2 supporte aussi property injection .

Injecter la request
New in version 2.4: Le service request_stack a t ajout dans la version 2.4 de Symfony.

Avec Symfony 2.4, au lieu d'injecter le service request, vous devez injecter le service request_stack et
accdez la Request en appellant la mthode getCurrentRequest()3:
Listing 16-19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

namespace Acme\HelloBundle\Newsletter;
use Symfony\Component\HttpFoundation\RequestStack;
class NewsletterManager
{
protected $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function anyMethod()
{
$request = $this->requestStack->getCurrentRequest();
// ... faire quelque chose avec la request
}

// ...
}

Dsormais, injecter seulement la request_stack, celle-ci se comportant comme tout service :


Listing 16-20

# src/Acme/HelloBundle/Resources/config/services.yml
services:
newsletter_manager:
class:
"Acme\HelloBundle\Newsletter\NewsletterManager"
arguments: ["@request_stack"]

3. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/RequestStack.html#getCurrentRequest()

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 236

Pourquoi ne pas injecter le service request ?


Presque tous les services internes Symfony2 se comportent de la mme faon: une seule instance
est cre par le conteneur qui le renvoie chaque fois que vous le demander ou quand il est inject
dans un autre service. Il a une exception dans une application Symfony2 standard : le service
request.
Si vous essayez d'injecter la request vous allez probablement recevoir une exception
ScopeWideningInjectionException4. Cela s'explique par le fait que la request peut changer
pendant le cycle de vie du conteneur de service. (lorsque d'une sous-requte est cre par exemple).

Si vous dfinissez un contrleur comme service, vous pourrez rcuprer l'objet Request sans l'avoir
inject dans le conteneur de service, mais en l'ayant pass comme argument de votre mthode
de contrleur (action). Pour plus de dtails, rendez-vous sur La Requte en tant qu'argument du
Contrleur.

Rendre les Rfrences Optionnelles


Parfois, un de vos services peut avoir une dpendance optionnelle, ce qui signifie que la dpendance n'est
pas requise par le service pour fonctionner correctement. Dans l'exemple ci-dessus, le service my_mailer
doit exister, sinon une exception sera leve. En modifiant les dfinitions du service newsletter_manager,
vous pouvez rendre cette rfrence optionnelle. Le conteneur va ensuite l'injecter si elle existe et ne rien
faire si ce n'est pas le cas :
Listing 16-21

1 # src/Acme/HelloBundle/Resources/config/services.yml
2 parameters:
3
# ...
4
5 services:
6
newsletter_manager:
7
class:
"%newsletter_manager.class%"
8
arguments: ["@?my_mailer"]

En YAML, la syntaxe spciale @? indique au conteneur de service que la dpendance est optionnelle. Bien
sr, le NewsletterManager doit tre aussi crit pour permettre une dpendance optionnelle :
Listing 16-22

1 public function __construct(Mailer $mailer = null)


2 {
3
// ...
4 }

Services de Bundle Tiers et Noyau de Symfony


tant donn que Symfony2 et tous les bundles tiers configurent et rcuprent leurs services via le
conteneur, vous pouvez facilement y accder, ou mme les utiliser dans vos propres services. Pour garder
les choses simples, par dfaut Symfony2 n'exige pas que les contrleurs soient dfinis comme des services.
Par ailleurs Symfony2 injecte l'ensemble du conteneur de services dans votre contrleur. Par exemple,

4. http://api.symfony.com/2.6/Symfony/Component/DependencyInjection/Exception/ScopeWideningInjectionException.html

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 237

pour grer le stockage des informations sur une session utilisateur, Symfony2 fournit un service session,
auquel vous pouvez accder de l'intrieur d'un contrleur standard comme suit :
Listing 16-23

1 public function indexAction($bar)


2 {
3
$session = $this->get('session');
4
$session->set('foo', $bar);
5
6
// ...
7 }

Dans Symfony2, vous allez constamment utiliser les services fournis par le noyau de Symfony ou autres
bundles tiers pour effectuer des tches telles que rendre des templates (templating), envoyer des emails
(mailer), ou d'accder des informations sur la requte (request).
Vous pouvez aller plus loin en utilisant ces services l'intrieur des services que vous avez crs pour
votre application. Modifions le NewsletterManager afin d'utiliser le vrai service mailer de Symfony2 (au
lieu du faux my_mailer). Passons aussi le service du moteur de template NewsletterManager afin qu'il
puisse gnrer le contenu de l'email via un template :
Listing 16-24

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

namespace Acme\HelloBundle\Newsletter;
use Symfony\Component\Templating\EngineInterface;
class NewsletterManager
{
protected $mailer;
protected $templating;
public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)
{
$this->mailer = $mailer;
$this->templating = $templating;
}

// ...
}

Configurer le conteneur de services est facile :


Listing 16-25

services:
newsletter_manager:
class:
"%newsletter_manager.class%"
arguments: [@mailer, @templating]

Le service newsletter_manager a dsormais accs aux services noyau mailer et templating. C'est une
faon commune de crer des services spcifiques votre application qui exploitent la puissance des
diffrents services au sein du framework.
Soyez sr que l'entre swiftmailer apparat dans votre configuration de l'application. Comme
vous l'avez mentionn dans Importer la Configuration via les Extensions de Conteneur, la cl
swiftmailer invoque l'extension du service de SwiftmailerBundle, qui dclare le service mailer.

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 238

Tags
De la mme manire qu'un billet de blog sur le Web pourrait tre tagu avec des noms telles que
Symfony ou PHP , les services configurs dans votre conteneur peuvent galement tre tagus. Dans
le conteneur de services, un tag laisse supposer que le service est cens tre utilis dans un but prcis.
Prenons l'exemple suivant :
Listing 16-26

1 services:
2
foo.twig.extension:
3
class: Acme\HelloBundle\Extension\FooExtension
4
tags:
5
- { name: twig.extension }

Le tag twig.extension est un tag spcial que le TwigBundle utilise pendant la configuration. En
donnant au service ce tag twig.extension, le bundle sait que le service foo.twig.extension devrait tre
enregistr comme une extension Twig avec Twig. En d'autres termes, Twig trouve tous les services taggs
avec twig.extension et les enregistre automatiquement comme des extensions.
Les tags, alors, sont un moyen de dire aux bundles de Symfony2 ou tiers que votre service doit tre
enregistr ou utilis d'une manire spciale par le bundle.
Ce qui suit est une liste de tags disponibles avec les bundles noyau de Symfony2. Chacun d'eux a un effet
diffrent sur votre service et de nombreux tags ncessitent des paramtres supplmentaires (au-del du
paramtre name).
Pour une liste de tous les tags disponibles dans le coeur du Framework Symfony, consultez Les Tags de
l'Injection de Dpendances.

Dbugger les services


Vous pouvez voir quels services sont enregistrs dans le conteneur grce la console. Pour afficher tous
les services et les classes de chacun d'entre eux, excutez :
Listing 16-27

1 $ php app/console container:debug

Par dfaut, seuls les services publics sont affichs, mais vous pouvez galement voir les services privs :
Listing 16-28

1 $ php app/console container:debug --show-private

Vous pouvez obtenir des informations dtailles sur un service particulier en spcifiant son identifiant :
Listing 16-29

1 $ php app/console container:debug my_mailer

Apprenez en plus

Compiler le Conteneur
Travailler avec les dfinitions du conteneur
Utiliser une Factory pour crer des services
Grer les dpendances communes avec des services parents
Travailler avec des Services Taggs
Comment dfinir des contrleurs en tant que Services
Comment travailler avec les champs d'applications ( scopes en anglais)

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 239

Comment travailler avec les Passes de Compilation dans les Bundles


Configuration Avance du Conteneur

PDF brought to you by


generated on January 14, 2015

Chapter 16: Service Container | 240

Chapter 17

Performance
Symfony2 est rapide, sans aucune configuration. Bien entendu, si vous voulez que a soit plus rapide, il
y a de nombreuses faons de rendre Symfony encore plus rapide. Dans ce chapitre, vous explorerez les
mthodes les plus courantes et les plus puissantes pour rendre votre application Symfony encore plus
rapide.

Utiliser un cache de byte code (ex APC)


L'une des meilleures choses (et des plus simples) que vous devriez faire pour augmenter la performance
est d'utiliser un cache de byte code ( byte code cache ). L'ide d'un cache de byte code est de ne plus
avoir recompiler constamment le code source PHP. Il existe un certain nombre de caches de byte code,
dont certains sont open source. Le cache de byte code le plus largement utilis est probablement APC1
Il n'y a aucun effet ngatif utiliser un cache de byte code, et Symfony2 a t conu pour tre vraiment
performant dans ce type d'environnement.

D'autres optimisations
Le cache de byte code surveille normalement tout changement de code source. Cela permet de recompiler
automatiquement le byte code en cas de changement d'un fichier source. C'est trs pratique, mais cela a
videmment un cot.
Pour cette raison, certains caches de byte code offrent l'option de dsactiver la vrification, ce sera donc
l'administrateur systme de vider le cache chaque changement de fichier. Sinon, les mises jour ne
seront pas visibles.
Par exemple, pour dsactiver cette vrification avec APC, ajoutez simplement apc.stat=0 votre fichier
de configuration php.ini.

1. http://php.net/manual/en/book.apc.php

PDF brought to you by


generated on January 14, 2015

Chapter 17: Performance | 241

Utiliser un chargeur automatique qui met en cache (par exemple


ApcUniversalClassLoader)
Par dfaut, la distribution Symfony standard utilise UniversalClassLoader dans le fichier
autoloader.php2. Ce chargeur automatique (autoloader) est facile utiliser, car il va automatiquement
trouver toute nouvelle classe que vous aurez place dans les rpertoires enregistrs.
Malheureusement, cela a un cot, et le chargeur itre travers tous les espaces de noms (namespaces)
pour trouver un fichier en particulier, en appelant file_exists jusqu' ce qu'il trouve le fichier qu'il
recherchait.
La solution la plus simple est de mettre en cache l'emplacement de chaque classe aprs qu'elles ont t
trouves la premire fois. Symfony est fourni avec une classe - ApcClassLoader3 - qui fait exactement
cela. Pour l'utiliser, il vous suffit d'adapter votre contrleur frontal. Si vous utilisez la distribution
standard, le code devrait dj tre disponible dans ce fichier en commentaires:
Listing 17-1

1
2
3
4
5
6
7
8
9
10
11
12
13

// app.php
// ...
$loader = require_once __DIR__.'/../app/bootstrap.php.cache';

// Use APC for autoloading to improve performance


// Change 'sf2' by the prefix you want in order to prevent key conflict with another
application
/*
$loader = new ApcClassLoader('sf2', $loader);
$loader->register(true);
*/
// ...

Lorsque vous utilisez le chargeur automatique APC, si vous avez de nouvelles classes, elles vont
tre trouves automatiquement et tout fonctionnera comme avant (donc aucune raison de vider le
cache). Par contre, si vous changez l'emplacement d'un espace de noms ou d'un prfixe, il va falloir
que vous vidiez le cache APC. Sinon, le chargeur automatique verra toujours l'ancien emplacement
des classes l'intrieur de ce namespace.

Utiliser des fichiers d'amorage


Afin d'assurer une flexibilit maximale et de favoriser la rutilisation du code, les applications Symfony2
profitent de nombreuses classes et composants externes. Mais charger toutes les classes depuis des
fichiers spars chaque requte peut entraner des cots. Afin de rduire ces cots, la distribution
Symfony standard fournit un script qui gnre ce qu'on appelle un fichier d'amorage4 ( bootstrap file
), qui consiste en une multitude de dfinitions de classes en un seul fichier. En incluant ce fichier (qui
contient une copie de nombreux fichiers du core), Symfony n'a plus besoin d'inclure les fichiers sources
individuels qui contiennent ces classes. Cela va rduire un peu les lectures/critures sur le disque.
Si vous utilisez l'dition Symfony standard, alors vous utilisez probablement dj le fichier d'amorage.
Pour vous en assurer, ouvrez votre contrleur frontal (gnralement app.php) et vrifiez que la ligne
suivante est prsente :
2. https://github.com/symfony/symfony-standard/blob/2.2/app/autoload.php
3. http://api.symfony.com/2.6/Symfony/Component/ClassLoader/ApcClassLoader.html
4. https://github.com/sensio/SensioDistributionBundle/blob/master/Composer/ScriptHandler.php

PDF brought to you by


generated on January 14, 2015

Chapter 17: Performance | 242

Listing 17-2

1 require_once __DIR__.'/../app/bootstrap.php.cache';

Veuillez noter qu'il y a deux inconvnients utiliser un fichier d'amorage :


le fichier ncessite d'tre rgnr chaque fois que les fichiers sources originaux changent (
savoir quand vous mettez jour le code source de Symfony2 ou une bibliothque tierce),
lors du dbogage, vous devrez placer des points d'arrt (breakpoints) dans ce fichier
d'amorage.
Si vous utilisez l'dition Symfony2 standard, les fichiers d'amorage sont automatiquement rgnrs
aprs avoir mis jour les bibliothques tierces ( vendors ) grce la commande composer.phar
install.

Fichiers d'amorage et caches de byte code


Mme en utilisant un cache de byte code, la performance sera amliore si vous utilisez un fichier
d'amorage, car il y aura moins de fichiers dont il faut surveiller les changements. Bien sr, si vous
dsactivez la surveillance des changements de fichiers (par exemple apc.stat=0 in APC), il n'y a plus de
raison d'utiliser un fichier d'amorage.

PDF brought to you by


generated on January 14, 2015

Chapter 17: Performance | 243

Chapter 18

Composants Internes
Visiblement vous voulez comprendre comment Symfony2 fonctionne et comment l'tendre. Cela me rend
trs heureux ! Cette section explique en profondeur les composants internes de Symfony2.
Vous avez besoin de lire cette section uniquement si vous souhaitez comprendre comment
Symfony2 fonctionne en arrire-plan, ou si vous voulez tendre Symfony2.

Vue Globale
Le code de Symfony2 se compose de plusieurs couches indpendantes. Chacune d'entre elles est
construite par-dessus celles qui la prcdent.
L'autoloading ( chargement automatique ) n'est pas gr par le framework directement ; ceci
est effectu indpendamment l'aide de la classe UniversalClassLoader1 et du fichier src/
autoload.php. Lisez le chapitre ddi pour plus d'informations.

Le Composant HttpFoundation
Le composant de plus bas niveau est HttpFoundation2. HttpFoundation fournit les objets principaux
ncessaires pour traiter avec HTTP. C'est une abstraction oriente objet de quelques fonctions et
variables PHP natives :
La classe Request3 abstrait les principales variables globales PHP que sont $_GET, $_POST,
$_COOKIE, $_FILES, et $_SERVER ;
Le classe Response4 abstrait quelques fonctions PHP comme header(), setcookie(), et echo
;
1. http://api.symfony.com/2.6/Symfony/Component/ClassLoader/UniversalClassLoader.html
2. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation.html
3. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Request.html
4. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 244

La classe Session5 et l'interface SessionStorageInterface6 font abstraction des fonctions de


gestion des sessions session_*().

Le Composant HttpKernel
Par-dessus HttpFoundation se trouve le composant HttpKernel7. HttpKernel gre la partie dynamique
de HTTP ; c'est une fine surcouche au-dessus des classes Request et Response pour standardiser la faon
dont les requtes sont gres. Il fournit aussi des points d'extension et des outils qui en font un point de
dmarrage idal pour crer un framework Web sans trop d'efforts.
Optionnellement, il ajoute de la configurabilit et de l'extensibilit, grce au composant Dependency
Injection et un puissant systme de plugin (bundles).
Apprenez en plus en lisant les chapitres Dependency Injection et Bundles.

Le Bundle FrameworkBundle
Le bundle FrameworkBundle8 est le bundle qui lie les principaux composants et bibliothques ensembles
afin de fournir un framework MVC lger et rapide. Il est fourni avec une configuration par dfaut ainsi
qu'avec des conventions afin d'en faciliter l'apprentissage.

Le Kernel
La classe HttpKernel9 est la classe centrale de Symfony2 et est responsable de la gestion des requtes
clientes. Son but principal est de convertir un objet Request10 en un objet Response11.
Chaque Kernel Symfony2 implmente HttpKernelInterface12
Listing 18-1

1 function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)

Les Contrleurs
Pour convertir une Requte en une Rponse, le Kernel repose sur un Contrleur . Un Contrleur peut
tre n'importe quel callable (code qui peut tre appel) PHP.
Le Kernel dlgue la slection de quel Contrleur devrait tre excut une implmentation de
ControllerResolverInterface13
Listing 18-2

1 public function getController(Request $request);


2
3 public function getArguments(Request $request, $controller);

La mthode getController()14 retourne le Contrleur (un callable PHP) associ la Requte


donne. L'implmentation par dfaut (ControllerResolver15) recherche un attribut de la requte
5. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Session.html
6. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/SessionStorage/SessionStorageInterface.html
7. http://api.symfony.com/2.6/Symfony/Component/HttpKernel.html
8. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle.html
9. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/HttpKernel.html
10. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Request.html
11. http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Response.html
12. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/HttpKernelInterface.html
13. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.html
14. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.html#getController()

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 245

_controller qui reprsente le nom du contrleur (une chane de caractres classe::mthode , comme
Bundle\BlogBundle\PostController:indexAction).
L'implmentation par dfaut utilise le RouterListener16 pour dfinir l'attribut de la Requte
_controller (voir L'vnement kernel.request).

La mthode getArguments()17 retourne un tableau d'arguments passer au Contrleur.


L'implmentation par dfaut rsout automatiquement les arguments de la mthode, base sur les
attributs de la Requte.

Faire correspondre les arguments de la mthode du Contrleur aux attributs de la Requte


Pour chaque argument d'une mthode, Symfony2 essaie d'obtenir la valeur d'un attribut d'une
Requte avec le mme nom. S'il n'est pas dfini, la valeur par dfaut de l'argument est utilise si elle
est dfinie
Listing 18-3

1
2
3
4
5
6

// Symfony2 va rechercher un attribut id (obligatoire)


// et un nomm admin (optionnel)
public function showAction($id, $admin = true)
{
// ...
}

Gestion des Requtes


La mthode handle()18 prend une Requte et retourne toujours une Rponse. Pour convertir la Requte,
handle() repose sur le Resolver et sur une chane ordonne de notifications d'vnements (voir la
prochaine section pour plus d'informations propos de chaque vnement) :
1. Avant de faire quoi que ce soit d'autre, l'vnement kernel.request est notifi -- si l'un des
listeners ( couteurs en franais) retourne une Rponse, il saute directement l'tape 8 ;
2. Le Resolver est appel pour dterminer le Contrleur excuter ;
3. Les listeners de l'vnement kernel.controller peuvent maintenant manipuler le callable
Contrleur de la manire dont ils souhaitent (le changer, crer un wrapper au-dessus de lui,
...) ;
4. Le Kernel vrifie que le Contrleur est un callable PHP valide ;
5. Le Resolver est appel pour dterminer les arguments passer au Contrleur ;
6. Le Kernel appelle le Contrleur ;
7. Si le Contrleur ne retourne pas une Rponse, les listeners de l'vnement kernel.view peuvent
convertir la valeur retourne par le Contrleur en une Rponse ;
8. Les listeners de l'vnement kernel.response peuvent manipuler la Rponse (contenu et enttes) ;
9. La Rponse est retourne.
Si une Exception est capture pendant le traitement de la Requte, l'vnement kernel.exception est
notifi et les listeners ont alors une chance de convertir l'Exception en une Rponse. Si cela fonctionne,
l'vnement kernel.response sera notifi ; sinon, l'Exception sera rejete.

15. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Controller/ControllerResolver.html
16. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/EventListener/RouterListener.html
17. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.html#getArguments()
18. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/HttpKernel.html#handle()

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 246

Si vous ne voulez pas que les Exceptions soient captures (pour des requtes imbriques par exemple),
dsactivez l'vnement kernel.exception en passant false en tant que troisime argument de la
mthode handle().

Requtes Internes
A tout moment, durant la gestion de la requte (la master ), une sous-requte peut tre gre. Vous
pouvez passer le type de requte la mthode handle() (son second argument) :
HttpKernelInterface::MASTER_REQUEST;
HttpKernelInterface::SUB_REQUEST.
Le type est pass tous les vnements et les listeners peuvent ainsi agir en consquence (le traitement
doit seulement intervenir sur la requte master ).

Les vnements
Chaque vnement captur par le Kernel est une sous-classe de KernelEvent19. Cela signifie que chaque
vnement a accs aux mmes informations de base :
getRequestType()20
retourne
le
type
de
la
requte
(HttpKernelInterface::MASTER_REQUEST ou HttpKernelInterface::SUB_REQUEST) ;
getKernel()21 - retourne le Kernel grant la requte ;
getRequest()22 - retourne la Requte courante qui est en train d'tre gre.

getRequestType()
La mthode getRequestType() permet aux listeners de connatre le type de la requte. Par exemple, si
un listener doit seulement tre activ pour les requtes master , ajoutez le code suivant au dbut de
votre mthode listener
Listing 18-4

1 use Symfony\Component\HttpKernel\HttpKernelInterface;
2
3 if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
4
// retourne immdiatement
5
return;
6 }

Si vous n'tes pas encore familier avec le Dispatcher d'vnements de Symfony2, lisez d'abord
la section Documentation du composant Event Dispatcher.

L'vnement kernel.request
La Classe vnement : GetResponseEvent23
Le but de cet vnement est soit de retourner un objet Response immdiatement, soit de dfinir des
variables afin qu'un Contrleur puisse tre appel aprs l'vnement. Tout listener peut retourner un
objet Response via la mthode setResponse() sur l'vnement. Dans ce cas, tous les autres listeners ne
seront pas appels.
19. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/KernelEvent.html
20. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/KernelEvent.html#getRequestType()
21. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/KernelEvent.html#getKernel()
22. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/KernelEvent.html#getRequest()
23. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/GetResponseEvent.html

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 247

Cet vnement est utilis par le FrameworkBundle afin de remplir l'attribut de la Requte _controller,
via RouterListener24. RequestListener utilise un objet RouterInterface25 pour faire correspondre la
Requte et dterminer le nom du Contrleur (stock dans l'attribut de la Requte _controller).

L'vnement kernel.controller
La Classe vnement: FilterControllerEvent26
Cet vnement n'est pas utilis par le FrameworkBundle, mais peut tre un point d'entre utilis pour
modifier le contrleur qui devrait tre excut:
Listing 18-5

1
2
3
4
5
6
7
8
9
10

use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
// ...

// le contrleur peut tre remplac par n'importe quel callable PHP


$event->setController($controller);
}

L'vnement kernel.view
La Classe vnement: GetResponseForControllerResultEvent27
Cet vnement n'est pas utilis par le FrameworkBundle, mais il peut tre utilis pour implmenter un
sous-systme de vues. Cet vnement est appel seulement si le Contrleur ne retourne pas un objet
Response. Le but de cet vnement est de permettre d'autres valeurs retournes d'tre converties en une
Rponse.
La valeur retourne par le Contrleur est accessible via la mthode getControllerResult
Listing 18-6

1
2
3
4
5
6
7
8
9
10
11
12

use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$val = $event->getReturnValue();
$response = new Response();
// personnalisez d'une manire ou d'une autre la Rponse
// en vous basant sur la valeur retourne
$event->setResponse($response);
}

L'vnement kernel.response
La Classe vnement: FilterResponseEvent28
L'objectif de cet vnement est de permettre d'autres systmes de modifier ou de remplacer l'objet
Response aprs sa cration :

24. http://api.symfony.com/2.6/Symfony/Bundle/FrameworkBundle/EventListener/RouterListener.html
25. http://api.symfony.com/2.6/Symfony/Component/Routing/RouterInterface.html
26. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/FilterControllerEvent.html
27. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.html
28. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/FilterResponseEvent.html

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 248

Listing 18-7

1 public function onKernelResponse(FilterResponseEvent $event)


2 {
3
$response = $event->getResponse();
4
// .. modifiez l'objet Response
5 }

Le FrameworkBundle enregistre plusieurs listeners :


ProfilerListener29: collecte les donnes pour la requte courante ;
WebDebugToolbarListener30: injecte la Barre d'Outils de Dbuggage Web ( Web Debug
Toolbar ) ;
ResponseListener31: dfinit la valeur du Content-Type de la Rponse base sur le format de
la requte ;
EsiListener32: ajoute un en-tte HTTP Surrogate-Control lorsque la Rponse a besoin
d'tre analyse pour trouver des balises ESI.

L'vnement kernel.exception
La Classe vnement: GetResponseForExceptionEvent33
Le FrameworkBundle enregistre un ExceptionListener34 qui transmet la Requte un Contrleur
donn (la valeur du paramtre exception_listener.controller -- doit tre exprim suivant la notation
class::method).
Un listener sur cet vnement peut crer et dfinir un objet Response, crer et dfinir un nouvel objet
Exception, ou ne rien faire :
Listing 18-8

1
2
3
4
5
6
7
8
9
10
11
12
13
14

use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
$response = new Response();
// dfinissez l'objet Response bas sur l'exception capture
$event->setResponse($response);

// vous pouvez alternativement dfinir une nouvelle Exception


// $exception = new \Exception('Some special exception');
// $event->setException($exception);
}

Comme Symfony vrifie que le code de statut de la Rponse est le plus appropri selon l'exception,
dfinir un code de statut sur la rponse ne fonctionnera pas. Si vous voulez rcrire le code de statut
(ce que vous ne devriez pas faire sans une excellente raison), dfinissez l'entte X-Status-Code:
Listing 18-9

1 return new Response('Error', 404 /* ignor */, array('X-Status-Code' => 200));

29. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/EventListener/ProfilerListener.html
30. http://api.symfony.com/2.6/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.html
31. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/EventListener/ResponseListener.html
32. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/EventListener/EsiListener.html
33. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Event/GetResponseForExceptionEvent.html
34. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/EventListener/ExceptionListener.html

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 249

Le Dispatcher d'vnements
Le dispatcher d'vnements est un composant autonome qui est responsable d'une bonne partie de la
logique sous-jacente et du flux d'une requte Symfony. Pour plus d'informations, lisez-la documentation
du composant Event Dispatcher.

Profiler
Lorsqu'il est activ, le profiler de Symfony2 collecte des informations utiles concernant chaque requte
envoye votre application et les stocke pour une analyse future. Utilisez le profiler dans l'environnement
de dveloppement afin de vous aider dbugger votre code et amliorer les performances de votre
application; utilisez-le dans l'environnement de production pour explorer des problmes aprs coup.
Vous avez rarement besoin d'interagir avec le profiler directement puisque Symfony2 vous fournit des
outils de visualisation tels la Barre d'Outils de Dbuggage Web ( Web Debug Toolbar ) et le Profiler
Web ( Web Profiler ). Si vous utilisez l'Edition Standard de Symfony2, le profiler, la barre d'outils de
dbuggage web, et le profiler web sont tous dj configurs avec des paramtres prdfinis.
Le profiler collecte des informations pour toutes les requtes (simples requtes, redirections,
exceptions, requtes Ajax, requtes ESI; et pour toutes les mthodes HTTP et tous les formats).
Cela signifie que pour une mme URL, vous pouvez avoir plusieurs donnes de profiling associes
(une par paire de requte/rponse externe).

Visualiser les Donnes de Profiling


Utiliser la Barre d'Outils de Dbuggage Web
Dans l'environnement de dveloppement, la barre d'outils de dbuggage web est disponible en bas
de toutes les pages. Elle affiche un bon rsum des donnes de profiling qui vous donne accs
instantanment plein d'informations utiles quand quelque chose ne fonctionne pas comme prvu.
Si le rsum fourni par la Barre d'Outils de Dbuggage Web n'est pas suffisant, cliquez sur le lien du jeton
(une chane de caractres compose de 13 caractres alatoires) pour pouvoir accder au Profiler Web.
Si le jeton n'est pas cliquable, cela signifie que les routes du profiler ne sont pas enregistres (voir
ci-dessous pour les informations concernant la configuration).

Analyser les donnes de Profiling avec le Profiler Web


Le Profiler Web est un outil de visualisation pour profiler des donnes que vous pouvez utiliser en
dveloppement pour dbugger votre code et amliorer les performances ; mais il peut aussi tre utilis
pour explorer des problmes qui surviennent en production. Il expose toutes les informations collectes
par le profiler via une interface web.

Accder aux informations de Profiling


Vous n'avez pas besoin d'utiliser l'outil de visualisation par dfaut pour accder aux informations de
profiling. Mais comment pouvez-vous obtenir les informations de profiling pour une requte spcifique
aprs coup ? Lorsque le profiler stocke les donnes concernant une Requte, il lui associe aussi un jeton ;
ce jeton est disponible dans l'en-tte HTTP X-Debug-Token de la Rponse
Listing 18-10

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 250

1 $profile = $container->get('profiler')->loadProfileFromResponse($response);
2
3 $profile = $container->get('profiler')->loadProfile($token);

Lorsque le profiler est activ, mais sans la barre d'outils de dbuggage web, ou lorsque vous voulez
rcuprer le jeton pour une requte Ajax, utilisez un outil comme Firebug pour obtenir la valeur de
l'en-tte HTTP X-Debug-Token.

Utilisez la mthode find()35 pour accder aux jetons bass sur quelques critres :
// rcupre les 10 derniers jetons $tokens = $container->get('profiler')->find('', '', 10);
// rcupre les 10 derniers jetons pour toutes les URL contenant /admin/ $tokens = $container>get('profiler')->find('', '/admin/', 10);
// rcupre les 10 derniers jetons pour les requtes locales $tokens = $container->get('profiler')>find('127.0.0.1', '', 10);
Si vous souhaitez manipuler les donnes de profiling sur une machine diffrente que celle o les
informations ont t gnres, utilisez les mthodes export()36 et import()37:
Listing 18-11

1
2
3
4
5
6

// sur la machine de production


$profile = $container->get('profiler')->loadProfile($token);
$data = $profiler->export($profile);
// sur la machine de dveloppement
$profiler->import($data);

Configuration
La configuration par dfaut de Symfony2 vient avec des paramtres prdfinis pour le profiler, la barre
d'outils de dbuggage web, et le profiler web. Voici par exemple la configuration pour l'environnement
de dveloppement :
Listing 18-12

1
2
3
4
5
6
7
8

# charge le profiler
framework:
profiler: { only_exceptions: false }
# active le profiler web
web_profiler:
toolbar: true
intercept_redirects: true

Quand l'option only-exceptions est dfinie comme true, le profiler collecte uniquement des donnes
lorsqu'une exception est capture par l'application.
Quand l'option intercept-redirects est dfinie true, le web profiler intercepte les redirections et
vous donne l'opportunit d'inspecter les donnes collectes avant de suivre la redirection.
Si vous activez le profiler web, vous avez aussi besoin de monter les routes du profiler :
35. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Profiler/Profiler.html#find()
36. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Profiler/Profiler.html#export()
37. http://api.symfony.com/2.6/Symfony/Component/HttpKernel/Profiler/Profiler.html#import()

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 251

Listing 18-13

_profiler:
resource: @WebProfilerBundle/Resources/config/routing/profiler.xml
prefix: /_profiler

Comme le profiler rajoute du traitement supplmentaire, vous pourriez vouloir l'activer uniquement
selon certaines circonstances dans l'environnement de production. Le paramtre only-exceptions limite
le profiling aux pages 500, mais qu'en est-il si vous voulez avoir les informations lorsque l'IP du client
provient d'une adresse spcifique, ou pour une portion limite du site web ? Vous pouvez utiliser la
correspondance de requte :
Listing 18-14

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# active le profiler uniquement pour les requtes venant du rseau 192.168.0.0


framework:
profiler:
matcher: { ip: 192.168.0.0/24 }
# active le profiler uniquement pour les URLs /admin
framework:
profiler:
matcher: { path: "^/admin/" }
# associe des rgles
framework:
profiler:
matcher: { ip: 192.168.0.0/24, path: "^/admin/" }
# utilise une instance de correspondance personnalise dfinie dans le
# service "custom_matcher"
framework:
profiler:
matcher: { service: custom_matcher }

En savoir plus grce au Cookbook

Comment utiliser le Profiler dans un test fonctionnel


Comment crer un Collecteur de Donnes personnalis
Comment tendre une Classe sans utiliser l'Hritage
Comment personnaliser le Comportement d'une Mthode sans utiliser l'Hritage

PDF brought to you by


generated on January 14, 2015

Chapter 18: Composants Internes | 252

Chapter 19

L'API stable de Symfony2


L'API stable de Symfony2 est un sous-ensemble de toutes les mthodes publiques de Symfony2
(composants et bundles "du coeur") qui partagent les proprits suivantes:

le namespace et le nom de la classe ne changeront pas,


le nom de la mthode ne changera pas,
la signature de la mthode (arguments et valeur de retour) ne changera pas,
l'objectif de la mthode ne changera pas

L'implmentation elle-mme peut changer. La seule raison valable d'un changement de l'API stable de
Symfony2 serait de corriger un problme de scurit.
L'API stable est base sur un principe de liste blanche ("whitelist"), tagge par @api. En consquence,
tout ce qui n'est pas explicitement tagu ne fait pas partie de l'API stable.
Chaque bundle tiers devrait aussi publier sa propre API stable.

Tout comme Symfony 2.0, les composants suivants ont leur propre API publique :

BrowserKit
ClassLoader
Console
CssSelector
DependencyInjection
DomCrawler
EventDispatcher
Finder
HttpFoundation
HttpKernel
Locale
Process
Routing
Templating
Translation

PDF brought to you by


generated on January 14, 2015

Chapter 19: L'API stable de Symfony2 | 253

Validator
Yaml

PDF brought to you by


generated on January 14, 2015

Chapter 19: L'API stable de Symfony2 | 254

Вам также может понравиться