Documentation officielle du framework PHP Symfony 3 - Partie 5

Partie 5 : Apprendre le paramétrage des contrôleurs dans Symfony 3

Symfony

Traducteur

Christophe LOUVET

Il s'agit de la traduction française de la documentation officielle du framework PHP Symfony 3. Vous allez apprendre à programmer avec ce puissant framework, de plus en plus utilisé, pour construire des applications d'envergure.

Dans cette sixième partie, vous allez apprendre à paramétrer et gérer les contrôleurs.

Si vous avez des remarques concernant ce tutoriel, un espace de dialogue vous est proposé sur le forum. N'hésitez pas à apporter vos avis sur le lien ci-dessous.

Commentez

- Titre : Documentation officielle du framework PHP Symfony 3 - Partie 5

- Auteur : Symfony

- Traducteur : Christophe LOUVET

- Parution : 29 novembre 2016

- Public visé :

- Licence : Le contenu de cet article est rédigé par Symfony et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Voir également :

- La quatrième partie : Apprendre à créer des pages Web avec Symfony 3

- Voir les autres parties

Sommaire

- 1. Introduction

- 2. Requêtes, contrôleur, cycle de vie des réponses

- 3. Un contrôleur simple

- 4. Mapper une URL à un contrôleur

- 4-1. Paramètres de route comme arguments de contrôleur

- 5. La classe de base de contrôleur et les services

- 5-1. Génération d'URL

- 5-2. Redirection

- 5-3. Le rendu de templates

- 5-4. Accéder à d'autres services

- 6. Gérer les erreurs et les pages 404

- 7. L'objet « Request » comme un argument du contrôleur

- 8. Gérer la session

- 8-1. Présentation générale de la gestion de session dans une application Symfony

- 8-2. Messages Flash

- 9. Les objets Request et Response

- 9-1. L'objet Response

- 9-2. L'objet Request

- 9-3. La méthode auxiliaire JSON

- 10. Créer des pages statiques

- 11. Forwarder à un autre contrôleur

- 12. Valider un jeton CSRF

- 13. Pensées finales

- Notes de la rédaction Developpez.com

1 - Introduction

Un contrôleur est une fonction PHP que vous créez et qui récupère l'information depuis l'objet Request d'une requête HTTP, puis crée et retourne une réponse HTTP (un objet Response Symfony).

La réponse peut être une page HTML, un document XML un tableau JSON sérialisé, une image, une redirection, une erreur 404, ou toute autre chose que vous pouvez imaginer.

Le contrôleur exécute quelque logique arbitraire selon les besoins de votre application pour interpréter le contenu de la page.

Voyons comment simplifier ceci en regardant un contrôleur Symfony en action. Ceci interprète une page qui affiche un numéro magique (numéro choisi au hasard) :

1. // src/AppBundle/Controller/MagiqueControleur.php

2. namespace AppBundle\Controller;

4. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

5. use Symfony\Component\HttpFoundation\Response;

7. class MagiqueControleur

8. {

9. /**

10. * @Route("/magique/numero")

11. */

12. public function numeroAction()

13. {

14. $numero = mt_rand(0, 100);

16. return new Response(

17. '<html><body>Numéro magique : '.$numero.'</body></html>'

18. );

19. }

20. }

Mais dans le vrai monde, un contrôleur sert, bien évidemment, à faire beaucoup de choses.

Le but d'un contrôleur est toujours le même : créer et retourner un objet Response. Le long de son parcours, il pourrait lire l'information depuis la requête, charger une ressource de base de données, envoyer un mail, ou placer une information sur la session utilisateur. Mais dans tous les cas, le contrôleur va éventuellement retourner l'objet Response qu'il va fournir en retour au client.

Si vous n'avez pas encore créé votre première page fonctionnelle, consulter cette partie de la documentation pour apprendre à créer des pages Web avec Symfony avant de poursuivre la lecture.

Il n'y a pas de magie et pas d'autres exigences dont il faut se soucier. Voici quelques exemples classiques de contrôleur :

- Le contrôleur A prépare un objet Response représentant le contenu de la page d'accueil du site ;

- le contrôleur B lit des paramètres depuis la requête pour charger une entrée du blog depuis la base de données et crée un objet Response affichant ce blog. Si les éléments indiqués dans les paramètres de l'objet Request ne peuvent être trouvés dans la base de données, il crée et retourne un objet Response avec le code de statut 404.

- Le contrôleur C gère la soumission de formulaire pour celui de contact. Il lit les informations de celui-ci depuis la requête, sauve les informations de contact dans la base de données et vous envoie les informations par mail. Il crée finalement un objet Response qui redirige le navigateur client vers la page « Merci » du formulaire de contact.

2 - Requêtes, contrôleur, cycle de vie des réponses

Chaque requête gérée par un projet Symfony passe par le même cycle de vie simple. Le Framework s'occupe de toutes les tâches répétitives : vous avez juste à écrire votre code personnalisé dans les fonctions contrôleur :

1. Chaque requête est gérée par un simple fichier contrôleur frontal (ex. : app.php ou app_dev.php) qui amorce l'application ;

2. le routeur lit les informations depuis la requête (ex. : l'URI), trouve une route qui correspond à l'information, et lit le paramètre du contrôleur depuis la route ;

3. le contrôleur est exécuté depuis la route correspondante et le code interne de celui-ci crée et retourne un objet Response ;

4. Les entêtes HTTP et le contenu de l'objet Response sont renvoyés au client.

5. Le contrôleur correct de la route correspondante est exécuté et le code de celui-ci crée et retourne l'objet Response approprié ;

6. Les entêtes HTTP et le contenu de l'objet Response sont renvoyés au client.

Créer une page est aussi simple que de créer un contrôleur, et que de faire une route qui mappe une URL à ce contrôleur.

Bien que portant le même nom, un « contrôleur frontal » est différent des contrôleurs évoqués dans ce chapitre. Un contrôleur frontal est un fichier PHP court qui réside dans votre dossier Web et par lequel toutes les requêtes sont dirigées. Une application typique va avoir un contrôleur frontal de production (ex. : app.php) et un contrôleur frontal de développement (ex. : app_dev.php). Vous n'aurez heureusement jamais besoin d'éditer, voir ou vous préoccuper des contrôleurs frontaux de votre application.

3 - Un contrôleur simple

Tandis qu'un contrôleur peut être n'importe quelle fonction PHP appelable (une fonction, méthode d'un objet, ou une Closure), un contrôleur est habituellement une méthode dans une classe contrôleur. Les contrôleurs sont aussi appelés actions.

1. // src/AppBundle/Controller/MagiqueControleur.php

2. namespace AppBundle\Controller;

4. use Symfony\Component\HttpFoundation\Response;

5. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

7. class MagiqueControleur

8. {

9. /**

10. * @Route("/magique/numero/{max}")

11. */

12. public function numeroAction($max)

13. {

14. $numero = mt_rand(0, $max);

16. return new Response(

17. '<html><body>Numéro Magique: '.$numero.'</body></html>'

18. );

19. }

20. }

Notez que le contrôleur est la méthode numeroAction, laquelle réside dans une classe controller : MagiqueControleur. Ne soyez pas embrouillé par le nommage : une classe controller est simplement un moyen pratique pour grouper ensemble plusieurs contrôleurs/actions. Typiquement, la classe controller va abriter plusieurs contrôleurs/actions (ex. : miseAjourAction, supprimerAction, etc.).

Ce contrôleur est assez simple :

- ligne 2 : Symfony profite de la fonctionnalité d'espace de noms de PHP pour créer un espace de noms pour toute la classe controller ;

- ligne 4 : Symfony profite également de la fonctionnalité d'espace de noms de PHP : le mot clé use importe la classe Response, celle que doit retourner le contrôleur ;

- ligne 7 : le nom de la classe est la concaténation du nom pour la classe controller (exemple : Magique) et du mot Controller, c'est une convention qui fournit de la consistance aux contrôleurs et leur permet d'être référencés uniquement par la première partie du nom (exemple : Magique) dans la configuration de routage. Toutefois, cette nomenclature n'est pas obligatoire pour le fonctionnement de la classe ;

- ligne 12 : chaque action dans une classe controller est suffixée par Action et est référencée dans la configuration de routage par son nom d'action (exemple : numero, dans la classe précédente). Cette nomenclature n'est également pas obligatoire pour le fonctionnement de la classe. Dans la prochaine section, vous allez créer une route qui mappe une URI pour cette action. Vous allez apprendre comment les spaces réservés ({name}) de route deviennent des arguments pour la méthode de l'action ($name) ;

- ligne 16 : Le contrôleur crée et retourne un objet Response.

4 - Mapper une URL à un contrôleur

Pour visualiser cette page dans votre navigateur, il faut créer une route, qui mappe un chemin d'URL spécifique au contrôleur.

Cela se fait avec l'annotation suivante :

1. @Route("/lucky/number/{max}")

Pour afficher la page de votre site, saisissez l'URL suivante dans votre navigateur :

http://localhost:8000/magique/numero/100

Le nouveau contrôleur retourne une simple page HTML.

Annotations

1. // src/AppBundle/Controller/MagiqueControleur.php

2. namespace AppBundle\Controller;

4. use Symfony\Component\HttpFoundation\Response;

5. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

7. class MagiqueControleur

8. {

9. /**

10. * @Route("/magique/{numero}", numero="magique")

11. */

12. public function numeroAction($numero)

13. {

14. return new Response('<html><body>Magique '.$numero.'!</body></html>');

15. }

16. }

Script YAML

1. # app/config/routing.yml

2. numero:

3. path:      /magique/{numero}

4. # uses a special syntax to point to the controller - see note below

5. defaults:  { _controller: AppBundle:Magique:Numero}

XML :

1. <!-- app/config/routing.xml -->

2. <?xml version="1.0" encoding="UTF-8" ?>

3. <routes xmlns="http://symfony.com/schema/routing"

4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5. xsi:schemaLocation="http://symfony.com/schema/routing

6. http://symfony.com/schema/routing/routing-1.0.xsd">

8. <route id="hello" path="/magique/{numero}">

9. <!-- uses a special syntax to point to the controller - see note below -->

10. <default key="_controller">AppBundle:Magique:Numero</default>

11. </route>

12. </routes>

Script PHP

1. // app/config/routing.php

2. use Symfony\Component\Routing\Route;

3. use Symfony\Component\Routing\RouteCollection;

5. $collection = new RouteCollection();

6. $collection->add('Magique', new Route('/Magique/{numero}', array(

7. // utilise une syntaxe spéciale pour pointer sur le contrôleur - voir note plus bas

8. '_controller' => 'AppBundle:Magique:Numero',

9. )));

11. return $collection;

Maintenant, vous pouvez aller à la page /magique/70 (ex. : http://localhost:8000/magique/70 si vous utilisez le serveur Web interne) et Symfony va exécuter le contrôleur MagiqueControleur::numeroAction() puis passer 70 à la variable $numero. Créer une « page » signifie simplement créer une méthode controller et lui associer une route.

Simple non ?

La syntaxe contrôleur : AppBundle:Magique:Numero.

Si vous utilisez les formats XML ou YML, vous vous référrerez au contrôleur en utilisant une syntaxe de raccourci spéciale : AppBundle:Magique:Numero.

Vous pouvez en apprendre plus sur le système de routage dans le chapitre suivant sur le routage.

4-1 - Paramètres de route comme arguments de contrôleur

Vous savez déjà que la route pointe vers la méthode MagiqueControleur::numeroAction() qui réside dans l'AppBundle. C'est plus intéressant si l'argument passé l'est à cette méthode :

1. // src/AppBundle/Controller/MagiqueControleur.php

2. // ...

3. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

5. /**

6. * @Route("/magique/{numero}", numero="magique")

7. */

8. public function numeroAction($numero)

9. {

10. // ...

11. }

Le contrôleur a un seul argument, $numero, lequel correspond au paramètre {numero} de la route (70 si vous vous rendez à l'adresse magique/70). Quand vous exécutez votre contrôleur, Symfony compare chaque argument avec un paramètre de la route. La valeur de {numero} est donc passée à $numero.

Voici un exemple plus intéressant avec deux arguments : le prénom et le nom.

Annotations :

1. // src/AppBundle/Controller/BonjourController.php

2. // ...

4. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

6. class BonjourController

7. {

8. /**

9. * @Route("/bonjour/{prenom}/{nom}", nom="bonjour")

10. */

11. public function indexAction($prenom, $nom)

12. {

13. // ...

14. }

15. }

YAML :

1. # app/config/routing.yml

2. hello:

3. path:      /bonjour/{prenom}/{nom}

4. defaults:  { _controller: AppBundle:Bonjour:index }

XML :

1. <!-- app/config/routing.xml -->

2. <?xml version="1.0" encoding="UTF-8" ?>

3. <routes xmlns="http://symfony.com/schema/routing"

4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

5. xsi:schemaLocation="http://symfony.com/schema/routing

6. http://symfony.com/schema/routing/routing-1.0.xsd">

8. <route id="hello" path="/bonjour/{prenom}/{nom}">

9. <default key="_controller">AppBundle:Bonjour:index</default>

10. </route>

11. </routes>

PHP :

1. // app/config/routing.php

2. use Symfony\Component\Routing\Route;

3. use Symfony\Component\Routing\RouteCollection;

5. $collection = new RouteCollection();

6. $collection->add('hello', new Route('/bonjour/{prenom}/{nom}', array(

7. '_controller' => 'AppBundle:Bonjour:index',

8. )));

10. return $collection;

Le contrôleur peut maintenant avoir deux arguments :

1. public function indexAction($prenom, $nom)

2. {

3. // ...

4. }

Mapper les paramètres de route aux arguments du contrôleur est simple et flexible. Gardez à l'esprit les lignes directrices suivantes quand vous programmez :

- L'ordre des arguments du contrôleur n'a pas d'importance.
Symfony fait correspondre les noms de paramètres de la route vers les noms de variables du contrôleur. Les arguments du contrôleur pourraient être totalement réorganisés et cela va toujours fonctionner :

1. public function indexAction($nom, $prenom)

2. {

3. // ...

4. }

- Chaque argument de contrôleur requis doit correspondre avec un paramètre de routage.
Le code suivant lèverait une RuntimeException, car il n'y a pas de paramètre foo défini dans la route :

public function indexAction($prenom, $nom, $foo)
{
// ...
}

- Par contre, rendre l'argument optionnel est parfaitement faisable. L'exemple suivant ne lèvera pas d'exception :

1. public function indexAction($prenom, $nom, $foo = 'bar')

2. {

3. // ...

4. }

- Tous les paramètres de routage n'ont pas besoin d'arguments dans votre contrôleur.
Si, par exemple, la variable nom n'est pas importante pour votre contrôleur, vous pourriez l'omettre entièrement :

1. public function indexAction($prenom)

2. {

3. // ...

4. }

Chaque route a également un paramètre spécial _route, qui est égal au nom de la route qui correspond (ex. : Bonjour). Bien que généralement inutile, ceci est aussi disponible comme un argument de contrôleur. Vous pouvez aussi passer les autres variables depuis votre route vers vos arguments de contrôleur. Consultez Comment passer des informations supplémentaires d'une route vers un contrôleur.

Vous aurez plus d'informations sur le routage dans le prochain chapitre dédié au routage.

5 - La classe de base de contrôleur et les services

Pour plus de commodités, Symfony est fourni avec une classe optionnelle de base Controller. Si vous l'étendez, cela ne changera rien au fonctionnement de votre contrôleur, mais vous aurez accès à un nombre de méthodes auxiliaires, et à tous les objets de votre service via le container (voir Accéder à d'autres services) : un objet de type tableau qui vous donne accès à tous les objets utiles du système. Ces objets nécessaires sont appelés services, et par exemple, Symfony est fourni avec un objet service qui peut retourner des templates Twig, un autre qui peut produire des logs, et d'autres qui peuvent faire bien plus.

Ajoutez la déclaration use devant la classe Controller puis modifiez le MagiqueControleur pour l'étendre :

// src/AppBundle/Controller/MagiqueControleur.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class MagiqueControleur extends Controller
{
// ...
}

Les méthodes auxiliaires sont juste des raccourcis pour utiliser les fonctionnalités du noyau de Symfony qui vous sont rendues disponibles avec ou sans l'utilisation de la classe Controller de base. Un excellent moyen de voir les fonctions noyau en action est de jeter un œil sur la classe Controller.

Si vous êtes curieux du fonctionnement normal d'un contrôleur n'étendant pas sa classe de base, consultez Controllers as Services. Ceci est optionnel, mais peut vous donner plus de contrôle sur les objets/dépendances injectés dans votre contrôleur.

5-1 - Génération d'URL

La méthode generateUrl() est une méthode auxiliaire qui permet de générer une URL à la route fournie. Par exemple :

1. $url = $this->generateUrl('affichage_blog', array('slug' => 'slug-value'));

5-2 - Redirection

Si vous voulez rediriger un utilisateur vers une autre page, utilisez les méthodes redirectToRoute() et redirect() :

1. public function indexAction()

2. {

3. // redirect to the "homepage" route

4. return $this->redirectToRoute('homepage');

6. // do a permanent - 301 redirect

7. return $this->redirectToRoute('homepage', array(), 301);

9. // redirect to a route with parameters

10. return $this->redirectToRoute('blog_show', array('slug' => 'my-page'));

12. // redirect externally

13. return $this->redirect('http://symfony.com/doc');

14. }

Le chapitre suivant sur le routage donnera plus d'informations.

La méthode redirectToRoute() est simplement un raccourci pour créer un objet Response, qui se spécialise dans la redirection de l'utilisateur. C'est équivalent à :

1. use Symfony\Component\HttpFoundation\RedirectResponse;

3. public function indexAction()

4. {

5. return new RedirectResponse($this->generateUrl('homepage'));

6. }

La méthode redirectToRoute() a été introduite depuis Symfony 2.6. Auparavant (et encore aujourd'hui), vous pouvez utiliser redirect() et generateUrl() ensemble pour cela.

Ou, si vous voulez rediriger de façon externe, utilisez juste redirect() et passez l'URL :

1. public function indexAction()

2. {

3. return $this->redirect('http://symfony.com/doc');

4. }

Par défaut, la méthode redirectToRoute() effectue une redirection 302 (temporaire). Pour effectuer une redirection 301 (permanente), modifiez le troisième argument :

1. public function indexAction()

2. {

3. return $this->redirectToRoute('homepage', array(), 301);

4. }

5-3 - Le rendu de templates

Si vous fournissez du HTML, vous voudrez interpréter un template. La méthode render() interprète un template et met son contenu dans un objet pour vous :

1. // renders app/Resources/views/magique/numero.html.twig

2. return $this->render('magique/numero.html.twig', array('numero' => $numero));

Les templates peuvent également se retrouver dans des sous-dossiers en profondeur, dans une arborescence. Essayez juste d'éviter de créer des structures profondes inutilement :

1. // renders app/Resources/views/hello/greetings/index.html.twig

2. return $this->render('hello/greetings/index.html.twig', array(

3. 'name' => $name

4. ));

Le moteur de templating de Symfony est expliqué en détail dans le chapitre sur les templates

Référencer des templates résidant dans le Bundle :

Vous pouvez aussi mettre des templates dans le dossier Resources/views d'un bundle et les référencer avec une syntaxe NomBundle:NomDossier:NomFichier. Par exemple, AppBundle:Hello:index.html.twig devrait se référer à un template situé dans le fichier src/AppBundle/Resources/views/Hello/index.html.twig.

5-4 - Accéder à d'autres services

Symfony est packagé avec beaucoup d'objets utiles, appelés services. Ceux-ci sont utilisés pour interpréter les templates, envoyer des mails, effectuer des requêtes dans des bases de données et d'autres « travaux » auxquels vous pourriez penser. Quand vous installez un nouveau Bundle, il apporte probablement encore plus de services.

Quand vous étendez la classe contrôleur de base, vous pouvez accéder à beaucoup de services via la méthode get(). Voici plusieurs services usuels dont vous pourriez avoir besoin :

1. $templating = $this->get('templating');

3. $router = $this->get('router');

5. $mailer = $this->get('mailer');

Quels autres services existent ? Pour lister tous les services, utilisez la commande console debug:container :

1. php bin/console debug:container

Sur les versions antérieures à Symfony 2.6, cette commande est nommée container:debug.

Vous aurez plus d'informations, sur le chapitre conteneur de services.

Pour obtenir un paramètre de la configuration du container, utilisez la méthode getParameter().

1. $from = $this->getParameter('app.mailer.from');

6 - Gérer les erreurs et les pages 404

Quand les objets ne sont pas trouvés, vous devriez en conformité avec le protocole HTTP retourner une réponse 404. Pour cela, vous lèverez un type spécial d'exception. Si vous étendez la classe de contrôleur de base, faites ceci :

1. public function indexAction()

2. {

3. // récupère un objet de la base de données

4. $product = ...;

5. if (!$product) {

6. throw $this->createNotFoundException("Le produit n'existe pas");

7. }

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

10. }

La méthode createNotFoundException() est juste un raccourci pour créer un objet spécial NotFoundHttpException, lequel au bout du compte intercepte les réponses HTTP 404 dans Symfony.

Bien sûr, vous pouvez lever toute classe d'exception dans votre contrôleur. Symfony va automatiquement retourner un code de réponse HTTP 500.

1. throw new \Exception("Quelque chose s'est mal passé !");

Dans tous les cas, une page d'erreur est affichée à l'utilisateur final et une page complète de débogage est présentée au développeur (ex. : quand vous utilisez app_dev.php, voir le chapitre sur l'installation de Symfony 3).

Vous allez vouloir personnaliser la page d'erreurs vue par les utilisateurs. Pour faire ceci, consultez : comment personnaliser les pages d'erreur.

7 - L'objet « Request » comme un argument du contrôleur

Qu'adviendra-t-il si vous voulez lire les paramètres de la requête, saisir un entête de requête ou obtenir l'accès à un fichier uploadé ? Toutes ces informations sont stockées dans l'objet « Request » de Symfony. Pour les obtenir dans votre contrôleur, il suffit de l'ajouter comme un argument et les fournir en entrée dans la classe « Request » :

1. use Symfony\Component\HttpFoundation\Request;

3. public function indexAction(Request $request, $prenom, $nom)

4. {

5. $page = $request->query->get('page', 1);

7. // ...

8. }

Vous aurez plus de détails sur l'objet « Request » dans la suite du tutoriel.

8 - Gérer la session

8-1 - Présentation générale de la gestion de session dans une application Symfony

Symfony fournit un super objet de session que vous pouvez utiliser pour stocker des informations à propos de l'utilisateur (que ce soit une vraie personne, un navigateur, un robot, ou un service Web) entre les requêtes. Par défaut, Symfony stocke les attributs dans un cookie en utilisant les sessions PHP natives.

Pour récupérer les données de la session, utiliser la méthode getSession() sur l'objet « Request ». Cette méthode retourne une SessionInterface avec des méthodes simples pour stocker et récupérer les informations depuis les objets de la session.

Stocker et récupérer des informations depuis la session peut être facilement fait depuis n'importe quel contrôleur :

1. use Symfony\Component\HttpFoundation\Request;

3. public function indexAction(Request $request)

4. {

5. $session = $request->getSession();

7. // stocke un attribut pour la réutilisation durant une prochaine requête utilisateur

8. $session->set('foo', 'bar');

10. // récupère un attribut fixé par un autre contrôleur dans une autre requête

11. $foobar = $session->get('foobar');

13. // utilise une valeur par défaut si l'attribut n'existe pas

14. $filters = $session->get('filters', array());

15. }

Les attributs stockés resteront disponibles dans cette session utilisateur.

8-2 - Messages Flash

Vous pouvez aussi stocker des messages spéciaux, nommés messages « flash », dans la session de l'utilisateur. Intentionnellement, les messages flash sont conçus pour un usage unique : ils disparaissent automatiquement de la session dès que vous les récupérez. Cette fonctionnalité fait que les messages « flash » sont particulièrement idéals pour stocker les notifications utilisateur.

Par exemple, imaginez que vous êtes en train de traiter la soumission d'un formulaire :

1. use Symfony\Component\HttpFoundation\Request;

3. public function updateAction(Request $request)

4. {

5. //...

6. $form = $this->createForm(...);

7. $form->handleRequest($request);

9. if ($form->isValid()) {

10. // fait une sorte de prétraitement

12. $this->addFlash(

13. 'notice',

14. 'Vos modifications ont été enregistrées !'

15. );

17. // $this->addFlash est équivalent à $this->get('session')->getFlashBag()->add

19. return $this->redirectToRoute(...);

20. }

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

23. }

Après traitement de la requête, le contrôleur affiche un message flash dans la session, puis effectue la redirection. Le message clé (« notice » dans cet exemple) peut être n'importe quoi : vous utiliserez cette clé pour récupérer le message.

Dans certains templates (comme vous le verrez dans la partie relative aux templates), il peut être nécessaire de lire tout message flash de la session. Par exemple :

Twig :

1. {# app/Resources/views/base.html.twig #}

2. {% pour un message flash_dans app.session.flashbag.get('notice') %}

3. <div class="flash-notice">

4. {{ flash_message }}

5. </div>

6. {% endfor %}

PHP  :

1. <?php foreach ($view['session']->getFlash('notice') as $message): ?>

2. <div class="flash-notice">

3. <?php echo "<div class='flash-error'>$message</div>" ?>

4. </div>

5. <?php endforeach ?>

Il est commun d'utiliser notice, warning et error comme mots clés des différents types de messages flash, mais vous pouvez utiliser d'autres mots clés comme vous le souhaitez.

Vous pouvez aussi utiliser la méthode peek() pour récupérer le message tout en la gardant dans le sac.

9 - Les objets Request et Response

9-1 - L'objet Response

La seule contrainte pour un contrôleur est de retourner un objet Response. Tout comme l'objet « Request », l'objet « Response » a aussi des propriétés d'entêtes publiques.

La classe Response est une abstraction autour d'une réponse HTTP : les entêtes et le contenu sont renseignés avec des messages textes qui sont renvoyés au client :

1. use Symfony\Component\HttpFoundation\Response;

3. // crée une simple réponse avec le code status 200 (code par défaut)

4. $response = new Response('Bonjour '.$nom, Response::HTTP_OK);

7. // crée une réponse CSS avec le code status 200

8. $response = new Response('<style> ... </style>');

9. $response->headers->set('Content-Type', 'text/css')

11. // crée une réponse JSON avec le code status 200

12. $response = new Response(json_encode(array('name' => $name)));

13. $response->headers->set('Content-Type', 'application/json');

La propriété headers de l'objet ResponseHeaderBag a des méthodes sympathiques pour obtenir et fixer les entêtes. Les noms de ces entêtes sont normalisés, et donc utiliser, par exemple, Content-Type est équivalent à content-type ou encore à content_type.

Il y a aussi des classes spéciales pour effectuer certains types de réponses plus facilement :

- Pour JSON il y a JsonResponse ;

- Pour les fichiers, il y a BinaryFileResponse ;

- Pour les réponses en flux, il y a StreamedResponse.

Ne vous inquiétez pas, il y a beaucoup d'informations à propos des objets Response dans la documentation des composants, consultez Response.

9-2 - L'objet Request

Comme mentionné plus haut, le framework Symfony peut passer l'objet « Request » comme un argument de contrôleur, qui sera saisi en entrée de la classe « Request ».

La classe « Request » a certaines propriétés publiques et des méthodes qui peuvent vous aider à retourner n'importe quelle information relative à votre requête.

Outre les valeurs de routage des places réservées, le contrôleur a aussi accès à l'objet Request. Le Framework injecte l'objet Request dans le contrôleur si une variable est préfixée Request :

1. use Symfony\Component\HttpFoundation\Request;

3. public function indexAction(Request $request)

4. {

5. $request->isXmlHttpRequest(); // Est ce une requête Ajax ?

7. $request->getPreferredLanguage(array('en', 'fr'));

10. $request->query->get('page'); // récupère un paramètre $_GET

12. $request->request->get('page'); // récupère un paramètre $_POST

15. // Récupérer les variables SERVER

16. $request->server->get('HTTP_HOST');

19. // Récupérer une instance de l'objet UploadedFile identifié par foo

20. $request->files->get('foo');

23. // Récupérer une valeur de COOKIE

24. $request->cookies->get('PHPSESSID');

27. // Récupérer un entête de requête HTTP avec de petites clés normalisées

28. $request->headers->get('host');

29. $request->headers->get('content_type');

30. }

Comme l'objet Response, la requête headers est stockée dans un objet HeaderBag et est facilement accessible.

Ne vous inquiétez pas, il y a beaucoup d'information à propos des objets Request dans la documentation composants. Consultez Request.

9-3 - La méthode auxiliaire JSON

La méthode auxiliaire json() a été introduite depuis Symfony 3.1.

Pour retourner un contenu JSON depuis le contrôleur, il faut utiliser la méthode auxiliaire json() dans le contrôleur de base. Cela retourne un spécial objet JsonResponse qui encode les données automatiquement :

1. // ...

2. public function indexAction()

3. {

4. // returns '{"username":"jane.doe"}' and sets the proper Content-Type header

5. return $this->json(array('username' => 'jane.doe'));

7. // the shortcut defines three optional arguments

8. // return $this->json($data, $status = 200, $headers = array(), $context = array());

9. }

Si le service de sérialisation est activé dans votre application, le contenu qui sera retourné de la méthode json() sera encodé par elle. Sinon la fonction json-encode sera utilisée.

Notez qu'il ne s'agit ici que des éléments de base sur les objets Request et Response. Vous aurez plus d'informations sur ces objets à la page relative au composant HttpFoundation.

10 - Créer des pages statiques

Vous pouvez créer une page statique sans même créer de contrôleur (seulement une route et un template sont requis).

Pour cela, consultez la page comment retourner un template sans contrôleur.

11 - Forwarder à un autre contrôleur

Bien que pas très commun, vous pouvez aussi forwarder vers un autre contrôleur en interne avec la méthode forward(). Plutôt que de rediriger le navigateur utilisateur, il effectue une sous-requête interne, et appelle le contrôleur. La méthode forward() retourne un objet Response qui est retourné depuis ce contrôleur :

public function indexAction($nom)
{
$response = $this->forward('AppBundle:Something:fancy', array(
'nom'  => $nom,
'couleur' => 'vert',
));

// ... modifie la réponse ou la retourne directement

return $response;
}

Notez que la méthode forward() utilise une chaîne spéciale de représentation du contrôleur (consultez Modèle de nommage de contrôleur). Dans ce cas, la fonction cible du contrôleur sera SomethingController::fancyAction() dans l'AppBundle. Le tableau passé à la méthode devient les arguments du contrôleur résultant. Cette même idée est utilisée lors de l'intégration de contrôleurs dans les templates (voir Contrôleurs embarqués).La méthode du contrôleur cible devrait ressembler à quelque chose comme cela :

1. public function fancyAction($nom, $couleur)

2. {

3. // ... créé et retourne un objet Response

4. }

Comme lors de la création d'un contrôleur pour une route, l'ordre des arguments de fancyAction n'a pas d'importance. Symfony fait correspondre les noms de clés d'index (ex. : nom) avec les noms d'arguments de la méthode (ex. : $nom). Si vous changez l'ordre des arguments, Symfony passera toujours les valeurs correctes à chaque variable.

12 - Valider un jeton CSRF

Quelquefois, vous voulez utiliser la protection CSRF dans une action ou vous ne voulez pas utiliser le composant formulaire de Symfony. Si, par exemple, vous faites une action DELETE, vous pouvez utiliser la méthode isCsrfTokenValid() pour contrôler le jeton CSRF :

1. if ($this->isCsrfTokenValid('token_id', $submittedToken)) {

2. // ... fait quelque chose, comme détruire un objet

3. }

La méthode raccourcie isCsrfTokenValid() a été introduite dans Symfony 2.6. Elle est équivalente à exécuter le code suivant :

1. use Symfony\Component\Security\Csrf\CsrfToken;

3. $this->get('security.csrf.token_manager')

4. ->isTokenValid(new CsrfToken('token_id', 'TOKEN'));

13 - Pensées finales

Chaque fois que vous créez une page, vous allez au bout du compte écrire du code qui contient la logique de cette page. Dans Symfony, ceci est appelé un contrôleur, et c'est une fonction PHP ou vous pouvez faire ce qu'il faut pour retourner l'objet de réponse final qui sera retourné à l'utilisateur.

Pour vous faciliter la vie, vous pouvez choisir d'étendre une classe contrôleur de base, laquelle contient des méthodes raccourcies pour beaucoup de tâches contrôleur classiques. Par exemple, si vous ne voulez pas mettre de code HTML dans votre contrôleur, vous pouvez utiliser la méthode render() pour interpréter et retourner le contenu depuis un template. En effet, vous bénéficiez de deux avantages principaux à étendre la classe du contrôleur de base :

- les méthodes raccourcies (comme render() et redirectToRoute) ;

- l'accès à tous les objets utiles (services) dans votre système grâce à la méthode get().

Dans les autres chapitres, vous verrez comment les contrôleurs peuvent être utilisés pour gérer des objets depuis une base de données, procéder à la soumission de formulaires, gérer le cache, et plus.

Notes de la rédaction Developpez.com

Nous tenons à remercier Sensiolabs pour la mise à disposition de cette documentation, Christophe Louvet pour la traduction et f-leb pour la relecture orthographique.