Contrôleurs Symfony - Pour faire fonctionner votre application

Nous avons pris notre template de Twig autonome autant que je le voudrais sans utiliser une méthode de contrôleur personnalisé.

Nous allons maintenant créer notre deuxième méthode de contrôleur personnalisé, et répliquer - puis améliorer - la fonctionnalité que nous avons vu jusqu'à présent.

Je vais commencer par supprimer l'route hello_page défini dans config/routes.yaml .

 # config/routes.yaml - hello_page: - path: /hello - controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController::templateAction - defaults: - template: hello_page.html.twig 

Cela casse momentanément notre page /hello .

Avant que klaxon ne commence à hurler, corrigeons rapidement ceci en créant une nouvelle méthode dans notre WelcomeController :

 <?php namespace App\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class WelcomeController extends AbstractController { /** * @Route("/", name="welcome") */ public function index() { return $this->render('welcome/index.html.twig'); } /** * @Route("/hello-page", name="hello_page") */ public function hello() { return $this->render('hello_page.html.twig'); } } 

Ok, donc quelques points intéressants à couvrir:

Nous aurions pu générer une nouvelle classe de contrôleur pour cette nouvelle route. Cela n'aurait pas d'importance. N'hésitez pas à le faire, si vous le souhaitez.

J'ai changé le chemin d'accès de /hello à /hello-page . Ceci est pour illustrer que changer le chemin ne fait aucune différence, tant que le nom de l'route reste le même.

Sur ce front, notre name route est resté le même - c'est toujours hello_page . Cela garde Twig heureux.

Notre nom de méthode - hello - peut vraiment être n'importe quoi. Tout, sauf index , qui est déjà utilisé.

Enfin, je n'ai pas déplacé le template hello_page.html.twig .

Je n'ai pas déplacé ce template pour illustrer le fait que les templates peuvent se trouver n'importe où dans le répertoire des templates . Il serait préférable, à mon avis, de déplacer ce template dans le sous-répertoire de welcome , simplement pour maintenir l'ordre à mesure que notre site se développe. Pour l'instant, je vais laisser ça tel quel.

  php bin/console debug:router -------------------------- -------- -------- ------ ----------------------------------- Name Method Scheme Host Path -------------------------- -------- -------- ------ ----------------------------------- welcome ANY ANY ANY / hello_page ANY ANY ANY /hello-page _twig_error_test ANY ANY ANY /_error/{code}.{_format} _wdt ANY ANY ANY /_wdt/{token} _profiler_home ANY ANY ANY /_profiler/ _profiler_search ANY ANY ANY /_profiler/search _profiler_search_bar ANY ANY ANY /_profiler/search_bar _profiler_phpinfo ANY ANY ANY /_profiler/phpinfo _profiler_search_results ANY ANY ANY /_profiler/{token}/search/results _profiler_open_file ANY ANY ANY /_profiler/open _profiler ANY ANY ANY /_profiler/{token} _profiler_router ANY ANY ANY /_profiler/{token}/router _profiler_exception ANY ANY ANY /_profiler/{token}/exception _profiler_exception_css ANY ANY ANY /_profiler/{token}/exception.css -------------------------- -------- -------- ------ ----------------------------------- 

Après avoir changé de route, il est toujours bon de valider la nouvelle configuration. Je le fais en utilisant bin/console debug:router .

La navigation autour du site montre maintenant que tout fonctionne encore, et notre "Hello Page" utilise maintenant le chemin mis à jour ( /hello-page ).

Passer des variables

Passons une variable de notre contrôleur et mettons à jour notre template hello_page.html.twig pour afficher cette variable:

 {% extends 'base.html.twig' %} {% block title %}Some custom title{% endblock title %} {% block body %} + <div> Hello, {{ app.request.query.get('name') | default('CodeReviewVideos') }}! + </div> + <div> + My lovely variable: {{ some_variable_name }} + </div> {% endblock %} 

J'ai ajouté les balises div supplémentaires ici pour rendre la sortie un peu meilleure.

Étant donné que j'utilise une variable du nom de some_variable_name dans mon template Twig, cela signifie que je dois transmettre cette variable - avec ce nom exact - à partir de ma méthode de contrôleur.

Si je ne le fais pas, je vais voir une grosse erreur rouge 500 :

une grosse erreur rouge 500

Comme nous sommes actuellement en mode développement, je vois une page d'erreur expliquant ce qui s'est passé et pourquoi:

"Variable" nom_variable "n'existe pas".

Nous avons vu une solution à cela dans la vidéo précédente: définir un default :

 {% extends 'base.html.twig' %} {% block title %}Some custom title{% endblock title %} {% block body %} <div> Hello, {{ app.request.query.get('name') | default('CodeReviewVideos') }}! </div> <div> My lovely variable: {{ some_variable_name | default('I am a lovely default value') }} </div> {% endblock %} 

Cela "corrige" le problème. Rafraîchissant maintenant, nous voyons:

"Ma belle variable: je suis une valeur par défaut"

Nous n'avons pas vraiment résolu le problème.

Heureusement, cela est facile.

Notre méthode de contrôleur return le résultat d'un appel à $this->render(...) :

  /** * @Route("/hello-page", name="hello_page") */ public function hello() { return $this->render('hello_page.html.twig'); } 

Si vous utilisez un IDE comme PhpStorm, ctrl + click render le fichier Symfony ControllerTrait.php où la méthode de render est définie.

De là, nous pouvons voir les arguments attendus de la méthode de render :

  /** * Renders a view. * * @final since version 3.4 */ protected function render(string $view, array $parameters = array(), Response $response = null): Response 

Le seul argument obligatoire est le premier argument: string $view .

Nous remplissons ce critère en passant dans notre nom de template, par exemple hello_page.html.twig .

Le deuxième argument est un array de $parameters .

C'est ainsi que nous transmettons nos variables dans le template.

Sachant cela, nous pouvons facilement passer dans notre paramètre some_variable_name :

  /** * @Route("/hello-page", name="hello_page") */ public function hello() { return $this->render('hello_page.html.twig', [ 'some_variable_name' => 'whatever we want here' ]); } 

Maintenant, rafraîchissez-vous encore une fois, et nous voyons:

"Ma belle variable: tout ce que nous voulons ici"

Facile, non?

Ces variables peuvent être aussi simples ou complexes que vous le souhaitez.

Répétant ce que nous avions

Dans la vidéo précédente, nous avons extrait le paramètre de requête name de l'URL et affiché le contenu dans notre template.

Si vous avez suivi, vous vous souviendrez que nous avons utilisé app.request.query.get('name') .

Nous pouvons également accéder à l'objet Request partir d'une méthode de contrôleur. Nous avons simplement besoin de l'injecter. Pour ce faire, nous fournissons le typehint correct, et définissons un nom de variable - généralement quelque chose de semblable au typehint:

 <?php namespace App\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\HttpFoundation\Request; class WelcomeController extends AbstractController { /** * @Route("/", name="welcome") */ public function index() { return $this->render('welcome/index.html.twig'); } /** * @Route("/hello-page", name="hello_page") */ - public function hello() + public function hello(Request $request) { return $this->render('hello_page.html.twig', [ 'some_variable_name' => 'whatever we want here' ]); } } 

Nous pouvons ensuite utiliser cet objet dans notre code.

Symfony est tout au sujet du voyage de la demande à la réponse.

Par conséquent, l'objet Request de Symfony est l'un des objets les plus utiles et les plus fréquemment utilisés avec lesquels vous travaillerez. L'injection de l'objet Request de cette manière est courante dans beaucoup de mes contrôleurs.

Un IDE décent comme PhpStorm vous aidera massivement à comprendre ce que l'objet Request offre en termes de méthodes et de propriétés.

Nous allons commencer par accéder à la propriété publique de la query . À partir de cette propriété de query , nous pouvons accéder aux paramètres de requête que nous avons définis dans la requête en cours. Comme un rappel rapide, les paramètres de requête sont ces choses sur l'URL après le ? , comme ?name=bob&age=25 .

Ceci est très similaire à ce que nous avons fait dans la vidéo précédente, seulement avec une syntaxe légèrement différente:

  /** * @Route("/hello-page", name="hello_page") */ public function hello(Request $request) { $someVar = $request->query->get('someVar'); return $this->render('hello_page.html.twig', [ 'some_variable_name' => $someVar, ]); } } 

Maintenant, si vous actualisez votre page, vous devriez soit voir la valeur par défaut de votre template, ou si vous naviguez vers http://127.0.0.1:8000/hello-page?someVar=whatever+goes+here alors:

"Ma belle variable: tout ce qui se passe ici"

Si vous avez suivi l'exemple supplémentaire de la vidéo précédente, alors, parce que la propriété name est utilisée directement dans notre template Twig, nous pouvons également le définir via le paramètre de requête still:

http://127.0.0.1:8000/hello-page?someVar=whatever+goes+here&name=Peter

Et puis nous voyons:

"Bonjour, Peter, ma belle variable: tout ce qui se passe ici"

Une dernière chose avant de passer à autre chose.

La variable locale $someVar est redondante. Nous pouvons intégrer l'appel à $request->query->get('someVar') :

  /** * @Route("/hello-page", name="hello_page") */ public function hello(Request $request) { return $this->render('hello_page.html.twig', [ 'some_variable_name' => $request->query->get('someVar'), ]); } 

Et tout devrait se comporter de la même manière.

Dans une application réelle, nous aurions besoin de valider toute entrée fournie telle qu'illustrée à l'aide de ces paramètres de requête. Nous pourrions ajouter cette logique à notre méthode de contrôleur. Ou nous pourrions utiliser la fonctionnalité qui nous est fournie dès le départ dans le cadre des annotations de routage de Symfony.

Route Placeholders

Nous avons utilisé des paramètres de requête jusqu'à présent. Il est non seulement plus joli, mais il nous offre aussi un tas de fonctionnalités supplémentaires si nous utilisons des espaces réservés pour le routage.

Ce que je veux dire ici est au lieu d'avoir:

http://127.0.0.1:8000/hello-page?name=Peter

Nous pourrions passer à:

http://127.0.0.1:8000/hello/CodeReviewVideos

Faisons ce changement:

  /** * @Route("/hello/{name}", name="hello_page") * @param string $name * * @return \Symfony\Component\HttpFoundation\Response */ public function hello(string $name) { return $this->render('hello_page.html.twig', [ 'person_name' => $name, ]); } 

Et mettez à jour le templates/hello_page.html.twig :

 {% extends 'base.html.twig' %} {% block title %}Some custom title{% endblock title %} {% block body %} <div> Hello, {{ person_name }}! </div> {% endblock %} 

Maintenant, si nous essayons de visiter par exemple http://127.0.0.1:8000/hello/chris , tout devrait être bon ici, non?

exception-has-been-thrown-pendant-le-rendu-d'-un-template-mandatory-parameters-missing

Bzzzzt. Mauvais monsieur! Faux.

Tout ce que nous faisions était techniquement correct. Toutefois:

Nous avons notre hello_page comme lien sur notre Navbar.

Afin de visiter la page hello_page , nous devons fournir un name pour répondre aux exigences d'espace réservé. Ceci est une donnée obligatoire , et nous n'avons pas fourni de valeur par défaut.

Heureusement, la réparation est aussi simple que de fournir une valeur par défaut dans l'argument $name méthode hello :

 - public function hello(string $name) + public function hello(string $name = 'CodeReviewVideos') 

Cool. Maintenant, essayez rafraîchissant et boom, notre page se charge comme prévu.

Si nous naviguons vers:

http://127.0.0.1:8000/hello/chris

Nous voyons "Bonjour, Chris!"

Si nous naviguons vers:

http://127.0.0.1:8000/hello

Nous voyons "Bonjour, CodeReviewVideos!"

Et si nous naviguons vers:

http://127.0.0.1:8000/hello/ (notez la barre oblique)

Nous obtenons une erreur 404 : 'Aucun route trouvé pour "GET / hello /"'

Ack. Eh bien, je vous le montre parce que c'est un problème du monde réel. Tout va bien grâce à des tutoriels qui vous montrent le chemin heureux. Je préfère une approche plus réaliste. Il y a une solution à ce problème. N'hésitez pas à l'ajouter à votre projet.

En note ici, il y a souvent plusieurs façons d'atteindre le même but lors de la programmation. Plutôt que de fournir une valeur par défaut à l'argument $name de votre méthode, vous pouvez également utiliser l'annotation par defaults , par exemple:

  /** * @Route("/hello/{name}", name="hello_page", defaults={"name" = "CodeReviewVideos"}) * @param string $name * * @return \Symfony\Component\HttpFoundation\Response */ public function hello(string $name) { return $this->render('hello_page.html.twig', [ 'person_name' => $name, ]); } 

Il n'y a pas de réelle différence ici, au meilleur de ma connaissance. Cependant, dans votre projet, il est préférable de choisir une approche et de s'y tenir.

Restrictions de routage

En ce moment, il est possible de naviguer vers:

http://127.0.0.1:8000/hello/chris%20123

Essayez-le.

Que vois-tu?

Eh bien, je te le dirai quand même, même si tu ne l'as pas fait.

Comme avant nous passons en chris , mais aussi l'URL codée représentation d'un espace ( %20 ), puis quelques chiffres ( 123 ).

Peut-être que nous ne voulons pas autoriser les espaces ou les nombres.

Symfony nous a couvert ici aussi.

  /** - * @Route("/hello/{name}", name="hello_page") + * @Route("/hello/{name}", name="hello_page", requirements={"name"="[A-Za-z]+"}) * @param string $name * * @return \Symfony\Component\HttpFoundation\Response */ public function hello(string $name = 'CodeReviewVideos') { return $this->render('hello_page.html.twig', [ 'person_name' => $name, ]); } 

En spécifiant nos requirements route requirements nous pouvons restreindre les routes sur lesquelles cette méthode de contrôleur va correspondre.

Dans ce cas, j'ai indiqué que le name espace réservé à notre route doit être un ou plusieurs caractères en majuscules ou en minuscules, de A à Z.

La partie délicate de la section des requirements est que vous devez utiliser une expression régulière pour votre match. J'aime les sites comme regexr pour m'aider à trouver des templates d'expression réguliers qui fonctionnent. Souvent, des exemples existent déjà pour résoudre la grande majorité des circonstances que vous rencontrerez. Mon conseil: gardez les choses simples.

Il y a plus à apprendre sur le routage dans Symfony 4, mais ce que nous avons couvert dans ce tutoriel et dans le précédent est plus que suffisant pour vous aider à démarrer.