Si vous cliquez sur une offre sur la page d'accueil Jobeet, l'URL ressemble à ceci: /job/1/show. Si vous avez déjà développé des sites web PHP, vous êtes probablement plus habitués à des URL comme /job.php?id=1. Comment Symfony les fait fonctionner? Comment Symfony détermine l'action à appeler à partir de cette URL? Pourquoi l'id de l'offre est-il récupéré avec le paramètre $id dans l'action? Ici, nous allons répondre à toutes ces questions.
Vous avez déjà vu le code suivant dans le template src/Ens/JobeetBundle/Resources/views/job/index.html.twig:
{{ path('ens_job_show', { 'id': entity.id }) }}
Celui-ci utilise la fonction de l'helper template path pour générer l'url pour l'offre qui a l'id 1. ens_job_show est le nom de la route utilisée, définie dans la configuration comme vous le verrez ci-dessous.
Dans Symfony2, la configuration du routage se fait habituellement dans le fichier app/config/routing.yml. Ceci importe la configuration du routage d'un paquet spécifique. Dans notre cas, le fichier src/Ens/JobeetBundle/Resources/config/routing.yml est importé:
# app/config/routing.yml EnsJobeetBundle: resource: "@EnsJobeetBundle/Resources/config/routing.yml" prefix: /
Maintenant, si vous regardez dans le fichier routing.yml de JobeetBundle vous verrez qu'il importe un autre fichier de routage, celui pour le contrôleur Job et définit une route appelée EnsJobeetBundle_homepage pour le modèle d'URL /hello/{name}:
# src/Ens/JobeetBundle/Resources/config/routing.yml EnsJobeetBundle_job: resource: "@EnsJobeetBundle/Resources/config/routing/job.yml" prefix: /job EnsJobeetBundle_homepage: pattern: /hello/{name} defaults: { _controller: EnsJobeetBundle:Default:index }
# src/Ens/JobeetBundle/Resources/config/routing/job.yml ens_job: pattern: / defaults: { _controller: "EnsJobeetBundle:Job:index" } ens_job_show: pattern: /{id}/show defaults: { _controller: "EnsJobeetBundle:Job:show" } ens_job_new: pattern: /new defaults: { _controller: "EnsJobeetBundle:Job:new" } ens_job_create: pattern: /create defaults: { _controller: "EnsJobeetBundle:Job:create" } requirements: { _method: post } ens_job_edit: pattern: /{id}/edit defaults: { _controller: "EnsJobeetBundle:Job:edit" } ens_job_update: pattern: /{id}/update defaults: { _controller: "EnsJobeetBundle:Job:update" } requirements: { _method: post } ens_job_delete: pattern: /{id}/delete defaults: { _controller: "EnsJobeetBundle:Job:delete" } requirements: { _method: post }
Voyons de plus près la route ens_job_show. Le modèle défini par ens_job_show agit comme /*/show où le joker est l'id. Pour l'URL /1/show, la variable id obtient une valeur de 1, qui est disponible pour vous permettre de l'utiliser dans votre contrôleur. Le paramètre _controller est une entrée spéciale qui dit à Symfony quel contrôleur/action doit être exécuté quand une URL correspond à cette route. Dans notre cas, il doit exécuter showAction de JobController dans EnsJobeetBundle.
Les paramètres de route (par exemple, {id}) sont particulièrement importants parce que chacun est mis à disposition en tant qu'argument à la méthode du contrôleur:
public function showAction($id) { // ... }
L'environnement de dev charge le fichier app/config/routing_dev.yml qui contient les routes utilisées par la Web Debug Toolbar, entre autres. Ce fichier charge, à la fin, le fichier de configuration principal routing.yml.
Si vous allez à http://jobeet.local/app_dev.php, vous verrez la page par défaut de Symfony. Nous allons changer cela pour qu'à l'avenir l'environnement de dev affiche les mêmes pages que celui de production. Ouvrez votre app/config/routing_dev.yml et enlevez les 3 premières routes qui sont relatives au paquet de démonstration de Symfony. Maintenant, le fichier routing_dev.yml devrait ressembler à ceci:
# app/config/routing_dev.yml _assetic: resource: . type: assetic _wdt: resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" prefix: /_wdt _profiler: resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" prefix: /_profiler _configurator: resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml" prefix: /_configurator _main: resource: routing.yml
N'oubliez pas de vider le cache après cette modification.
php app/console cache:clear --env=prod php app/console cache:clear --env=dev
Pour l'instant, lorsque vous demandez l'URL / dans un navigateur, vous obtenez une erreur 404. C'est parce que cette URL ne correspond à aucune des routes définies. Nous avons une route EnsJobeetBundle_homepage qui correspond à l'URL /hello/jobeet et nous envoie à l'action index de DefaultController. Nous allons la changer pour correspondre à l'URL / et appeler l'action index de JobController. Pour faire le changement, modifiez comme ci-dessous:
# src/Ens/JobeetBundle/Resources/config/routing.yml # ... EnsJobeetBundle_homepage: pattern: / defaults: { _controller: EnsJobeetBundle:Job:index }
Maintenant, si vous effacez le cache et allez à http://jobeet.local, vous verrez la page d'accueil Job. Nous pouvons maintenant modifier le lien du logo Jobeet dans le layout pour utiliser la route EnsJobeetBundle_homepage:
<!-- src/Ens/JobeetBundle/Resources/views/layout.html.twig --> <!-- ... --> <h1><a href="{{ path('EnsJobeetBundle_homepage') }}"> <img src="{{ asset('bundles/ensjobeet/images/logo.jpg') }}" alt="Jobeet Job Board" /> </a></h1> <!-- ... -->
Pour quelque chose d'un peu plus compliqué, nous allons changer l'URL d'une offre avec quelque chose de plus significatif:
/job/sensio-labs/paris-france/1/web-developer
Sans rien savoir de Jobeet, et sans regarder la page, vous pouvez comprendre à partir de l'URL que Sensio Labs est à la recherche d'un Développeur Web pour travailler à Paris, France.
Le schéma ci-dessous correspond à une telle URL:
/job/{company}/{location}/{id}/{position}
Modifiez la route ens_job_show à partir du fichier job.yml:
# src/Ens/JobeetBundle/Resources/config/routing/job.yml # ... ens_job_show: pattern: /{company}/{location}/{id}/{position} defaults: { _controller: "EnsJobeetBundle:Job:show" }
Maintenant, nous avons besoin de passer tous les paramètres de la route modifiée pour que cela fonctionne:
<!-- src/Ens/JobeetBundle/Resources/views/Job/index.html.twig --> <!-- ... --> <a href="{{ path('ens_job_show', { 'id': entity.id, 'company': entity.company, 'location': entity.location, 'position': entity.position }) }}"> {{ entity.position }} </a> <!-- ... -->
Si vous regardez les URLs générées, elles ne sont pas encore tout à fait comme nous voulons qu'elles soient:
http://jobeet.local/job/Sensio Labs/Paris, France/1/Web Developer
Nous avons besoin de "remodeler" les valeurs de chaque colonne en remplaçant tous les caractères non ASCII par un tiret (-). Ouvrez le fichier Job.php et ajoutez les méthodes suivantes à la classe:
// src/Ens/JobeetBundle/Entity/Job.php // ... use Ens\JobeetBundle\Utils\Jobeet as Jobeet; class Job { // ... public function getCompanySlug() { return Jobeet::slugify($this->getCompany()); } public function getPositionSlug() { return Jobeet::slugify($this->getPosition()); } public function getLocationSlug() { return Jobeet::slugify($this->getLocation()); } }
Ensuite, créez le fichier src/Ens/JobeetBundle/Utils/Jobeet.php et ajoutez la méthode slugify():
// src/Ens/JobeetBundle/Utils/Jobeet.php namespace Ens\JobeetBundle\Utils; class Jobeet { static public function slugify($text) { // replace all non letters or digits by - $text = preg_replace('/\W+/', '-', $text); // trim and lowercase $text = strtolower(trim($text, '-')); return $text; } }
Nous avons défini trois nouveaux accesseurs "virtuels": getCompanySlug(), getPositionSlug(), et getLocationSlug(). Ils retournent la valeur correspondant à leur colonne après avoir appliqué la méthode slugify(). Maintenant, vous pouvez remplacer les noms réels des colonnes par les virtuels dans le modèle:
<!-- src/Ens/JobeetBundle/Resources/views/Job/index.html.twig --> <!-- ... --> <a href="{{ path('ens_job_show', { 'id': entity.id, 'company': entity.companySlug, 'location': entity.locationSlug, 'position': entity.positionSlug}) }}"> {{ entity.position }} </a> <!-- ... -->
Vous aurez maintenant l'URL attendue:
http://jobeet.local/app_dev.php/job/sensio-labs/paris-france/1/web-developer
Le système de routage a une fonction de validation intégrée. Chaque modèle de variable peut être validé par une expression régulière définie en utilisant l'entrée requirements d'une définition de route:
# src/Ens/JobeetBundle/Resources/config/routing/job.yml # ... ens_job_show: pattern: /{company}/{location}/{id}/{position} defaults: { _controller: "EnsJobeetBundle:Job:show" } requirements: id: \d+
L'entrée requirements ci-dessus oblige l'id à être une valeur numérique. Sinon, la route ne fonctionne pas.
Bien que l'ajout et la personnalisation de routes soit parlant, il est utile de pouvoir visualiser et obtenir des informations détaillées sur vos routes. Un excellent moyen de voir toutes les routes dans votre application se fait via la commande router:debug. Exécutez la commande en la lançant à partir de la racine de votre projet:
php app/console router:debug
La commande permet d'afficher une liste utile de toutes les routes configurées dans votre application. Vous pouvez également obtenir des renseignements très précis sur une seul route en incluant le nom de la route après la commande:
php app/console router:debug ens_job_show
Pour en savoir plus sur le système de routage Symfony2, lisez le chapitre Routing de la documentation officielle.