contrôleur

Un contrôleur est une fonction PHP que vous créez qui lit les informations de l'objet Request et crée et renvoie un objet Response . La réponse pourrait être une page HTML, JSON, XML, un téléchargement de fichier, une redirection, une erreur 404 ou toute autre chose que vous pouvez imaginer. Le contrôleur exécute toute logique arbitraire dont votre application a besoin pour afficher le contenu d'une page.

enlightened Si vous n'avez pas encore créé votre première page de travail, consultez Créer votre première page dans Symfony , puis revenez!

Un contrôleur simple

Alors qu'un contrôleur peut être n'importe quel callable de PHP (une fonction, une méthode sur un objet ou une Closure ), un contrôleur est généralement une méthode dans une classe de contrôleur:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 // src/Controller/LuckyController.php
namespace App\Controller ;

use Symfony\Component\HttpFoundation\Response ;
use Symfony\Component\Routing\Annotation\Route ;

class LuckyController
{
    /**
     * @Route("/lucky/number/{max}", name="app_lucky_number")
     */
    public function number($max)
    {
        $number = mt_rand(0 , $max);

        return new Response (
            '<html><body>Lucky number: ' . $number . '</body></html>'
        );
    }
}

Le contrôleur est la méthode number() , qui réside dans une classe de contrôleur LuckyController .

Ce contrôleur est assez simple:

  • ligne 2 : Symfony tire parti de la fonctionnalité d'espace de noms de PHP pour l'espace de noms de toute la classe du contrôleur.
  • ligne 4 : Symfony tire à nouveau parti de la fonctionnalité d'espace de noms de PHP: le mot clé use importe la classe Response , que le contrôleur doit renvoyer.
  • line 7 : La classe peut techniquement s'appeler n'importe quoi, mais elle est suffixée avec Controller par convention.
  • ligne 12 : La méthode action est autorisée à avoir un argument $max grâce au caractère générique {max} dans la route .
  • ligne 16 : le contrôleur crée et renvoie un objet Response .

Mappage d'une URL à un contrôleur

Pour afficher le résultat de ce contrôleur, vous devez lui associer une URL via une route. Cela a été fait ci-dessus avec le @Route("/lucky/number/{max}") .

Pour voir votre page, accédez à cette URL dans votre navigateur:

Pour plus d'informations sur le routage, voir Routage .

Les classes et services du contrôleur de base

Pour rendre la vie plus agréable, Symfony est livré avec deux contrôleurs  de base optionnels : Controller et AbstractController . Vous pouvez les étendre pour avoir accès à certaines méthodes d'aide .

Ajoutez l'instruction use au sommet de votre classe de contrôleur, puis modifiez LuckyController pour l'étendre:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// src/Controller/LuckyController.php
namespace App\Controller;

+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;

- class LuckyController
+ class LuckyController extends Controller
{
    // ...
}

C'est tout! Vous avez maintenant accès à des méthodes comme $ this-> render () et bien d'autres que vous apprendrez par la suite.

enlightened Quelle est la différence entre Controller ou AbstractController ? Pas grand chose: les deux sont identiques, sauf que AbstractController est plus restrictif: il ne vous permet pas d'accéder directement aux services via $this->get() ou $this->container->get() . Cela vous oblige à écrire du code plus robuste pour accéder aux services. Mais si vous avez besoin d'un accès direct au conteneur, l'utilisation de Controller est correcte.

Génération d'URL

La méthode generateUrl() est simplement une méthode d'assistance qui génère l'URL d'une route donnée:

 $url = $this->generateUrl('app_lucky_number' , array('max' => 10));

Redirection

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 use Symfony\Component\HttpFoundation\RedirectResponse ;

// ...
public function index()
{
    // redirects to the "homepage" route
    return $this->redirectToRoute('homepage');

    // redirectToRoute is a shortcut for:
    // return new RedirectResponse($this->generateUrl('homepage'));

    // does a permanent - 301 redirect
    return $this->redirectToRoute('homepage' , array(), 301);

    // redirect to a route with parameters
    return $this->redirectToRoute('app_lucky_number' , array('max' => 10));

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

Mise en garde

La méthode redirect() ne vérifie pas sa destination de quelque façon que ce soit. Si vous redirigez vers une URL fournie par les utilisateurs finaux, votre application peut être ouverte à la vulnérabilité de sécurité des redirections non validées .

Rendu des templates

Si vous diffusez du code HTML, vous devez afficher un template. La méthode render() affiche un template et place ce contenu dans un objet Response pour vous:

 // renders templates/lucky/number.html.twig
return $this->render('lucky/number.html.twig' , array('number'=>$number));

Templating et Twig sont expliqués plus en détail dans l'article Création et utilisation de templates .

Récupérer des services

Symfony est livré avec beaucoup d'objets utiles, appelés services . Ceux-ci sont utilisés pour rendre des templates, envoyer des courriels, interroger la base de données et tout autre «travail» que vous pouvez penser.

Si vous avez besoin d'un service dans un contrôleur, tapez simplement un argument avec son nom de classe (ou d'interface). Symfony vous transmettra automatiquement le service dont vous avez besoin:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 use Psr\Log\LoggerInterface
// ...

/**
 * @Route("/lucky/number/{max}")
 */
public function number($max , LoggerInterface $logger)
{
    $logger->info('We are logging!');
    // ...
}

Impressionnant!

Quels autres services pouvez-vous taper? Pour les voir, utilisez la commande debug:autowiring console:

  php bin/console debug:autowiring

Si vous avez besoin de contrôler la valeur exacte d'un argument, vous pouvez lier l'argument par son nom:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
     # config/services.yaml
    services :
        # ...
    
        # explicitly configure the service
        App\Controller\LuckyController :
            public : true
            bind :
                # for any $logger argument, pass this specific service
                $logger : '@monolog.logger.doctrine'
    
  •  

Vous pouvez bien sûr aussi utiliser l' injection de constructeur normale dans vos contrôleurs.

 

Mise en garde

Vous pouvez uniquement transmettre des services à vos arguments de contrôleur de cette manière. Il n'est pas possible, par exemple, de passer un paramètre de service en tant qu'argument du contrôleur, même en utilisant bind . Si vous avez besoin d'un paramètre, utilisez le raccourci $this->getParameter('kernel.debug') ou passez la valeur via la méthode __construct() votre contrôleur et spécifiez sa valeur avec bind .

Pour plus d'informations sur les services, consultez l'article Service Container .

Générer des contrôleurs

Pour gagner du temps, vous pouvez également demander à Symfony de générer une nouvelle classe de contrôleur:

  php bin/console make:controller BrandNewController

created: src/Controller/BrandNewController.php

Si vous voulez générer un CRUD entier à partir d'une entité Doctrine, utilisez:

  php bin/console make:crud Product

Nouveauté 1.2: La commande make:crud été introduite dans MakerBundle 1.2.

Gestion des erreurs et des 404 pages

Quand les choses ne sont pas trouvées, vous devriez retourner une réponse 404. Pour ce faire, lancez un type spécial d'exception:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException ;

// ...
public function index ()
{
    // retrieve the object from database
    $product = ... ;
    if ( ! $product ) {
        throw $this -> createNotFoundException ( 'The product does not exist' );

        // the above is just a shortcut for:
        // throw new NotFoundHttpException('The product does not exist');
    }

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

La méthode createNotFoundException() est juste un raccourci pour créer un objet NotFoundHttpException spécial, qui déclenche finalement une réponse HTTP 404 à l'intérieur de Symfony.

Si vous lancez une exception qui s'étend ou est une instance de HttpException , Symfony utilisera le code d'état HTTP approprié. Sinon, la réponse aura un code d'état HTTP de 500:

 // this exception ultimately generates a 500 status error
throw new \Exception ( 'Something went wrong!' );

Dans tous les cas, une page d'erreur est montrée à l'utilisateur final et une page d'erreur de débogage complète est montrée au développeur (c.-à-d. En mode "Debug" - voir Les paramètres Key: Parameters (Variables) ).

Pour personnaliser la page d'erreur qui s'affiche à l'utilisateur, consultez l'article Comment personnaliser les pages d'erreur .

L'objet Request en tant qu'argument du contrôleur

Que se passe-t-il si vous avez besoin de lire des paramètres de requête, de récupérer un en-tête de requête ou d'accéder à un fichier téléchargé? Toutes ces informations sont stockées dans l'objet Request de Symfony. Pour l'obtenir dans votre contrôleur, ajoutez-le simplement en argument et tapez-le avec la classe Request :

1
2
3
4
5
6
7
8
 use Symfony\Component\HttpFoundation\Request ;

public function index ( Request $request , $firstName , $lastName )
{
    $page = $request -> query -> get ( 'page' , 1 );

    // ...
}

Continuez à lire pour plus d'informations sur l'utilisation de l'objet Demande.

Gérer la session

Symfony fournit un service de session que vous pouvez utiliser pour stocker des informations sur l'utilisateur entre les demandes. Le stockage de session et toute autre configuration peuvent être contrôlés dans la configuration de framework.session .

Tout d'abord, activez la session en décommentant la clé de session dans config/packages/framework.yaml :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# config/packages/framework.yaml
framework:
    # ...

-     #session:
-     #    # The native PHP session handler will be used
-     #    handler_id: ~
+     session:
+         # The native PHP session handler will be used
+         handler_id: ~
    # ...

Pour obtenir la session, ajoutez un argument et tapez-le avec SessionInterface :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 use Symfony\Component\HttpFoundation\Session\SessionInterface ;

public function index ( SessionInterface $session )
{
    // stores an attribute for reuse during a later user request
    $session -> set ( 'foo' , 'bar' );

    // gets the attribute set by another controller in another request
    $foobar = $session -> get ( 'foobar' );

    // uses a default value if the attribute doesn't exist
    $filters = $session -> get ( 'filters' , array ());
}

Les attributs stockés restent dans la session pour le reste de la session de cet utilisateur.

 

Pointe

Chaque implémentation SessionInterface est prise en charge. Si vous possédez votre propre implémentation, tapez-le dans l'argument à la place.

Pour plus d'informations, voir Sessions .

Messages Flash

Vous pouvez également stocker des messages spéciaux, appelés "flash", sur la session de l'utilisateur. De par leur conception, les messages flash sont destinés à être utilisés une seule fois: ils disparaissent automatiquement de la session dès que vous les récupérez. Cette fonctionnalité rend les messages "flash" particulièrement intéressants pour stocker les notifications des utilisateurs.

Par exemple, imaginez que vous traitez une soumission de formulaire :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 use Symfony\Component\HttpFoundation\Request ;

public function update ( Request $request )
{
    // ...

    if ( $form -> isSubmitted () && $form -> isValid ()) {
        // do some sort of processing

        $this -> addFlash (
            'notice' ,
            'Your changes were saved!'
        );
        // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()

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

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

Après le traitement de la demande, le contrôleur définit un message flash dans la session, puis redirige. La clé de message ( notice dans cet exemple) peut être n'importe quoi: vous utiliserez cette clé pour récupérer le message.

Dans le template de la page suivante (ou mieux, dans votre template de mise en page de base), lisez les messages flash de la session en utilisant app.flashes() :

  • Brindille
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
     {# templates/base.html.twig #}
    
    {# you can read and display just one flash message type... #}
    {% for message in app.flashes ( 'notice' ) %}
        <div class= "flash-notice" >
            {{ message }}
        </div>
    {% endfor %}
    
    {# ...or you can read and display every flash message available #}
    {% for label , messages in app.flashes %}
        {% for message in messages %}
            <div class= "flash- {{ label }} " >
                {{ message }}
            </div>
        {% endfor %}
    {% endfor %}
    
  • PHP
     

Il est courant d'utiliser les notice , les warning et les error comme clés des différents types de messages flash, mais vous pouvez utiliser n'importe quelle touche correspondant à vos besoins.

 

Pointe

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

L'objet de requête et de réponse

Comme mentionné précédemment , Symfony transmettra l'objet Request à tout argument de contrôleur dont le type est indiqué avec la classe Request :

 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
 use Symfony\Component\HttpFoundation\Request ;

public function index ( Request $request )
{
    $request -> isXmlHttpRequest (); // is it an Ajax request?

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

    // retrieves GET and POST variables respectively
    $request -> query -> get ( 'page' );
    $request -> request -> get ( 'page' );

    // retrieves SERVER variables
    $request -> server -> get ( 'HTTP_HOST' );

    // retrieves an instance of UploadedFile identified by foo
    $request -> files -> get ( 'foo' );

    // retrieves a COOKIE value
    $request -> cookies -> get ( 'PHPSESSID' );

    // retrieves an HTTP request header, with normalized, lowercase keys
    $request -> headers -> get ( 'host' );
    $request -> headers -> get ( 'content_type' );
}

La classe Request possède plusieurs propriétés et méthodes publiques qui renvoient toutes les informations dont vous avez besoin sur la requête.

Comme la Request , l'objet Response a également une propriété d'en- headers publics. Ceci est un ResponseHeaderBag qui a quelques bonnes méthodes pour obtenir et définir les en-têtes de réponse. Les noms d'en-tête sont normalisés de sorte que l'utilisation de Content-Type soit équivalente à content-type ou même content_type .

La seule exigence pour un contrôleur est de renvoyer un objet Response :

1
2
3
4
5
6
7
8
 use Symfony\Component\HttpFoundation\Response ;

// creates a simple Response with a 200 status code (the default)
$response = new Response ( 'Hello ' . $name , Response :: HTTP_OK );

// creates a CSS-response with a 200 status code
$response = new Response ( '<style> ... </style>' );
$response -> headers -> set ( 'Content-Type' , 'text/css' );

Il existe des classes spéciales qui facilitent certains types de réponses. Certains d'entre eux sont mentionnés ci-dessous. Pour en savoir plus sur la Request et la Response (et les classes de Response spéciales), consultez la documentation du composant HttpFoundation .

Retour de la réponse JSON

Pour renvoyer JSON à partir d'un contrôleur, utilisez la méthode d'assistance json() . Cela renvoie un objet JsonResponse spécial qui code les données automatiquement:

1
2
3
4
5
6
7
8
9
 // ...
public function index ()
{
    // returns '{"username":"jane.doe"}' and sets the proper Content-Type header
    return $this -> json ( array ( 'username' => 'jane.doe' ));

    // the shortcut defines three optional arguments
    // return $this->json($data, $status = 200, $headers = array(), $context = array());
}

Si le service de sérialiseur est activé dans votre application, il sera utilisé pour sérialiser les données dans JSON. Sinon, la fonction json_encode est utilisée.

Streaming des réponses de fichiers

Vous pouvez utiliser l'assistant file() pour servir un fichier depuis un contrôleur:

1
2
3
4
5
 public function download ()
{
    // send the file contents and force the browser to download it
    return $this -> file ( '/path/to/some_file.pdf' );
}

L'assistant file() fournit des arguments pour configurer son comportement:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 use Symfony\Component\HttpFoundation\File\File ;
use Symfony\Component\HttpFoundation\ResponseHeaderBag ;

public function download ()
{
    // load the file from the filesystem
    $file = new File ( '/path/to/some_file.pdf' );

    return $this -> file ( $file );

    // rename the downloaded file
    return $this -> file ( $file , 'custom_name.pdf' );

    // display the file contents in the browser instead of downloading it
    return $this -> file ( 'invoice_3241.pdf' , 'my_invoice.pdf' , ResponseHeaderBag :: DISPOSITION_INLINE );
}

Réflexions finales

Chaque fois que vous créez une page, vous aurez finalement besoin d'écrire du code qui contient la logique de cette page. Dans Symfony, on appelle cela un contrôleur, et c'est une fonction PHP où vous pouvez faire n'importe quoi pour retourner l'objet Response final qui sera retourné à l'utilisateur.

Pour vous faciliter la vie, vous allez probablement étendre la classe de Controller base, car elle donne accès à des méthodes de raccourci (comme render() et redirectToRoute() ).

Dans d'autres articles, vous apprendrez comment utiliser des services spécifiques à l'intérieur de votre contrôleur, ce qui vous aidera à persister et récupérer des objets d'une base de données, à traiter des soumissions de formulaires, à gérer la mise en cache, etc.

Continue!

Ensuite, apprenez tout sur les templates de rendu avec Twig .

En savoir plus sur les contrôleurs