Licence CC BY-NC-SA

Vous avez dit Symfony2 ?

Dans ce chapitre, nous allons voir comment est organisé Symfony2 à l'intérieur. Nous n'entrerons pas dans les détails, c'est trop tôt, le but est juste d'avoir une vision globale du processus d'exécution d'une page sous Symfony2. Ainsi, vous pourrez comprendre ce que vous faites. C'est mieux, non ? :)

L'architecture des fichiers

On vient d'extraire beaucoup de fichiers, mais sans savoir encore à quoi ils servent. C'est le moment d'éclaircir tout cela !

Liste des répertoires

Ouvrez donc le répertoire dans lequel vous avez extrait les fichiers. Vous pouvez voir qu'il n'y a pas beaucoup de fichiers ici, seulement des répertoires. En effet, tout est bien rangé dans chaque répertoire, il nous faut donc comprendre à quoi ils servent. En voici la liste :

  • app
  • src
  • vendor
  • web

Le répertoire /app

Ce répertoire contient tout ce qui concerne votre site internet… sauf son code source. Assez étrange, me direz-vous. En fait, c'est simplement pour séparer le code source, qui fait la logique de votre site, du reste. Le reste, c'est ce répertoire /app. Et ce reste c'est : la configuration, le cache, les fichiers logs, etc. Ce sont des fichiers qui concernent l'entièreté de votre site, contrairement aux fichiers de code source qui seront découpés par fonctionnalité de votre site. Dans Symfony2, un projet de site internet est une application, simple question de vocabulaire. Le répertoire /app est donc le raccourci pour « application ».

Le répertoire /src

Voici enfin le répertoire dans lequel on mettra le code source ! C'est ici que l'on passera le plus clair de notre temps. Dans ce répertoire, nous organiserons notre code en bundles, des briques de notre application, dont nous verrons la définition plus loin.

Vous pouvez voir que ce répertoire n'est pas vide : il contient en effet quelques fichiers exemples, fournis par Symfony2. Nous les supprimerons plus tard dans ce cours.

Le répertoire /vendor

Ce répertoire contient toutes les bibliothèques externes à notre application. Dans ces bibliothèques externes, j'inclus Symfony2 ! Vous pouvez parcourir ce répertoire, vous y trouverez des bibliothèques comme Doctrine, Twig, SwiftMailer, etc.

Et une bibliothèque, c'est quoi exactement ?

Une bibliothèque est une sorte de boîte noire qui remplit une fonction bien précise, et dont on peut se servir dans notre code. Par exemple, la bibliothèque SwiftMailer permet d'envoyer des e-mails. On ne sait pas comment elle fonctionne (principe de la boîte noire), mais on sait comment s'en servir : on pourra donc envoyer des e-mails très facilement, juste en apprenant rapidement à utiliser la bibliothèque.

Le répertoire /web

Ce répertoire contient tous les fichiers destinés à vos visiteurs : images, fichiers CSS et JavaScript, etc. Il contient également le contrôleur frontal (app.php), dont nous parlerons juste après.

En fait, c'est le seul répertoire qui devrait être accessible à vos visiteurs. Les autres répertoires ne sont pas censés être accessibles (ce sont vos classes, elles vous regardent vous, pas vos visiteurs), c'est pourquoi vous y trouverez des fichiers .htaccess interdisant l'accès depuis l'extérieur. On utilisera donc toujours des URL du type http://localhost/Symfony/web/… au lieu de simplement http://localhost/Symfony/… .

Si vous le souhaitez, vous pouvez configurer votre Apache pour que l'URL http://localhost/Symfony pointe directement sur le répertoire /web. Pour cela, vous pouvez lire ce tutoriel qui explique comment configurer Apache. Cependant, ce n'est pas très important pour le développement, on en reparlera plus loin.

À retenir

Retenez donc que nous passerons la plupart de notre temps dans le répertoire /src, à travailler sur nos bundles. On touchera également pas mal au répertoire /app pour configurer notre application. Et lorsque nous installerons des bundles téléchargés, nous le ferons dans le répertoire /vendor.

Le contrôleur frontal

Définition

Le contrôleur frontal (front controller, en anglais) est le point d'entrée de votre application. C'est le fichier par lequel passent toutes vos pages. Vous devez surement connaître le principe d'index.php et des pseudo-frames (avec des URL du type index.php?page=blog) ; eh bien, cet index.php est un contrôleur frontal. Dans Symfony2, le contrôleur frontal se situe dans le répertoire /web, il s'agit de app.php ou app_dev.php.

Pourquoi y a-t-il deux contrôleurs frontaux ? Normalement, c'est un fichier unique qui gère toutes les pages, non ?

Vous avez parfaitement raison… pour un code classique ! Mais nous travaillons maintenant avec Symfony2, et son objectif est de nous faciliter le développement. C'est pourquoi Symfony2 propose un contrôleur frontal pour nos visiteurs, app.php, et un contrôleur frontal lorsque nous développons, app_dev.php. Ces deux contrôleurs frontaux, fournis par Symfony2 et prêts à l'emploi, définissent en fait deux environnements de travail.

Deux environnements de travail

L'objectif est de répondre au mieux suivant la personne qui visite le site :

  • Un développeur a besoin d'informations sur la page afin de l'aider à développer. En cas d'erreur, il veut tous les détails pour pouvoir déboguer facilement. Il n'a pas besoin de rapidité.
  • Un visiteur normal n'a pas besoin d'informations particulières sur la page. En cas d'erreur, l'origine de celle-ci ne l'intéresse pas du tout, il veut juste retourner d'où il vient. Par contre, il veut que le site soit le plus rapide possible à charger.

Vous voyez la différence ? À chacun ses besoins, et Symfony2 compte bien tous les remplir. C'est pourquoi il offre plusieurs environnements de travail :

  • L'environnement de développement, appelé « dev », accessible en utilisant le contrôleur frontal app_dev.php. C'est l'environnement que l'on utilisera toujours pour développer.
  • L'environnement de production, appelé « prod », accessible en utilisant le contrôleur frontal app.php.

Essayez-les ! Allez sur http://localhost/Symfony/web/app_dev.php et vous verrez une barre d'outils en bas de votre écran, contenant nombre d'informations utiles au développement. Allez sur http://localhost/Symfony/web/app.php et vous obtiendrez… une erreur 404. :p En effet, aucune page n'est définie par défaut pour le mode « prod ». Nous les définirons plus tard, mais notez que c'est une « belle » erreur 404, aucun terme barbare n'est employé pour la justifier. ^^

Pour voir le comportement du mode « dev » en cas d'erreur, essayez aussi d'aller sur une page qui n'existe pas. Vous avez vu ce que donne une page introuvable en mode « prod », mais allez maintenant sur /app_dev.php/pagequinexistepas. La différence est claire : le mode « prod » nous dit juste « page introuvable » alors que le mode « dev » nous donne plein d'informations sur l'origine de l'erreur, indispensables pour la corriger.

C'est pourquoi, dans la suite du tutoriel, nous utiliserons toujours le mode « dev », en passant donc par app_dev.php. Bien sûr, lorsque votre site sera opérationnel et que des internautes pourront le visiter, il faudra leur faire utiliser le mode « prod ». Mais nous n'en sommes pas encore là.

Et comment savoir quelles erreurs surviennent en mode production si elles ne s'affichent pas ?

C'est une bonne question, en effet si par malheur une erreur intervient pour l'un de vos visiteurs, il ne verra aucun message et vous non plus, une vraie galère pour déboguer ! En réalité, si les erreurs ne sont pas affichées, elles sont bien stockées dans un fichier. Allez jeter un œil au fichier app/logs/prod.log qui contient plein d'informations sur les requêtes effectuées en mode production, dont les erreurs.

Concrètement, qu'est-ce que contrôle le contrôleur frontal ?

Très bonne question. Pour cela, rien de tel que… d'ouvrir le fichier app.php. Ouvrez-le et vous constaterez qu'il ne fait pas grand-chose. En effet, le but du contrôleur frontal n'est pas de faire quelque chose, mais d'être un point d'entrée de notre application. Il se limite donc à appeler le noyau (Kernel) de Symfony2 en disant « On vient de recevoir une requête, transforme-la en réponse s'il-te-plaît. »

Ici, voyez le contrôleur frontal comme un fichier à nous (il est dans notre répertoire /web), et le Kernel comme un composant Symfony2, une boîte noire (il est dans le répertoire /vendor). Vous voyez comment on a utilisé notre premier composant Symfony2 : on a délégué la gestion de la requête au Kernel. Bien sûr, ce Kernel aura besoin de nous pour savoir quoi exécuter comme code, mais il gère déjà plusieurs choses que nous avons vues : la gestion des erreurs, l'ajout de la toolbar en bas de l'écran, etc. On n'a encore rien fait, et pourtant on a déjà gagné du temps !

L'architecture conceptuelle

On vient de voir comment sont organisés les fichiers de Symfony2. Maintenant, il s'agit de comprendre comment s'organise l'exécution du code au sein de Symfony2.

Architecture MVC

Vous avez certainement déjà entendu parler de ce concept. Sachez que Symfony2 respecte bien entendu cette architecture MVC. Je ne vais pas entrer dans ses détails, car il y a déjà un super cours sur le Site du Zéro, mais en voici les grandes lignes.

MVC signifie « Modèle / Vue / Contrôleur ». C'est un découpage très répandu pour développer les sites internet, car il sépare les couches selon leur logique propre :

  • Le Contrôleur (ou Controller) : son rôle est de générer la réponse à la requête HTTP demandée par notre visiteur. Il est la couche qui se charge d'analyser et de traiter la requête de l'utilisateur. Le contrôleur contient la logique de notre site internet et va se contenter « d'utiliser » les autres composants : les modèles et les vues. Concrètement, un contrôleur va récupérer, par exemple, les informations sur l'utilisateur courant, vérifier qu'il a le droit de modifier tel article, récupérer cet article et demander la page du formulaire d'édition de l'article. C'est tout bête, avec quelques if(), on s'en sort très bien.
  • Le Modèle (ou Model) : son rôle est de gérer vos données et votre contenu. Reprenons l'exemple de l'article. Lorsque je dis « le contrôleur récupère l'article », il va en fait faire appel au modèle Article et lui dire : « donne-moi l'article portant l'id 5 ». C'est le modèle qui sait comment récupérer cet article, généralement via une requête au serveur SQL, mais ce pourrait être depuis un fichier texte ou ce que vous voulez. Au final, il permet au contrôleur de manipuler les articles, mais sans savoir comment les articles sont stockés, gérés, etc. C'est une couche d'abstraction.
  • La Vue (ou View) : son rôle est d'afficher les pages. Reprenons encore l'exemple de l'article. Ce n'est pas le contrôleur qui affiche le formulaire, il ne fait qu'appeler la bonne vue. Si nous avons une vue Formulaire, les balises HTML du formulaire d'édition de l'article y seront et au final le contrôleur ne fera qu'afficher cette vue sans savoir vraiment ce qu'il y a dedans. En pratique, c'est le designer d'un projet qui travaille sur les vues. Séparer vues et contrôleurs permet aux designers et développeurs PHP de travailler ensemble sans se marcher dessus.

Au final, si vous avez bien compris, le contrôleur ne contient que du code très simple, car il se contente d'utiliser des modèles et des vues en leur attribuant des tâches précises. Il agit un peu comme un chef d'orchestre, qui n'agite qu'une baguette alors que ses musiciens jouent des instruments complexes.

Parcours d'une requête dans Symfony2

Afin de bien visualiser tous les acteurs que nous avons vus jusqu'à présent, je vous propose un schéma du parcours complet d'une requête dans Symfony2 :

Parcours complet d'une requête dans Symfony2

En le parcourant avec des mots, voici ce que cela donne :

  1. Le visiteur demande la page /blog ;
  2. Le contrôleur frontal reçoit la requête, charge le Kernel et la lui transmet ;
  3. Le Kernel demande au Routeur quel contrôleur exécuter pour l'URL /blog. Ce Routeur est un composant Symfony2 qui fait la correspondance entre URL et contrôleurs, nous l'étudierons bien sûr dans un prochain chapitre. Le Routeur fait donc son travail, et dit au Kernel qu'il faut exécuter le contrôleur SdzBlog ;
  4. Le Kernel exécute donc ce contrôleur. Le contrôleur demande au modèle Article la liste des articles, puis la donne à la vue ListeArticles pour qu'elle construise la page HTML et la lui retourne. Une fois cela fini, le contrôleur envoie au visiteur la page HTML complète.

J'ai mis des couleurs pour distinguer les points où l'on intervient. En vert donc, les contrôleur, modèle et vue, c'est ce qu'on devra développer nous-mêmes. En orange donc, le Kernel et le Routeur, c'est ce qu'on devra configurer. On ne touchera pas au contrôleur frontal, en gris.

Maintenant, il ne nous reste plus qu'à voir comment organiser concrètement notre code et sa configuration.

Symfony2 et ses bundles

La découpe en bundles

Le concept

Vous avez déjà croisé ce terme de bundle quelques fois depuis le début du cours, mais qu'est-ce qui se cache derrière ce terme ?

Pour faire simple, un bundle est une brique de votre application. Symfony2 utilise ce concept novateur qui consiste à regrouper dans un même endroit, le bundle, tout ce qui concerne une même fonctionnalité. Par exemple, on peut imaginer un bundle « Blog » dans notre site, qui regrouperait les contrôleurs, les modèles, les vues, les fichiers CSS et JavaScript, etc. Tout ce qui concerne directement la fonctionnalité blog de notre site.

Cette organisation permet de découper naturellement nos fonctionnalités, et ainsi de ranger chaque fichier à sa place. Un fichier JavaScript n'est utilisé que sur le bundle Blog ? Mettez-le dans le bundle Blog ! Bien évidemment, au sein d'un bundle, il faut retrouver également une architecture bien définie, nous l'étudierons juste après.

Des exemples

Pour mieux visualiser, je vous propose quelques bons exemples de bundles possibles :

  • Un bundle Utilisateur, qui va gérer les utilisateurs ainsi que les groupes, intégrer des pages d'administration de ces utilisateurs, et des pages classiques comme le formulaire d'inscription, de récupération de mot de passe, etc.
  • Un bundle Blog, qui va fournir une interface pour gérer un blog sur le site. Ce bundle peut utiliser le bundle Utilisateur pour faire un lien vers les profils des auteurs des articles et des commentaires.
  • Un bundle Boutique, qui va fournir des outils pour gérer des produits et des commandes.
  • Un bundle Admin, qui va fournir uniquement une interface vers les outils d'administration des différents bundles utilisés (Utilisateur, Blog, etc.). Attention, il ne doit pas y avoir beaucoup de code dans ce bundle, ce n'est qu'un raccourci vers les fonctionnalités d'administration des autres bundles. La partie admin pour ajouter un article au blog doit se trouver dans le bundle Blog.

Et ces bundles, parce qu'ils respectent des règles communes, vont fonctionner ensemble. Par exemple, un bundle Forum et un bundle Utilisateur devront s'entendre : dans un forum, ce sont des utilisateurs qui interagissent. ;)

L'intérêt

Une question à toujours se poser : quel est l'intérêt de ce que l'on est en train de faire ?

En plus d'organiser votre code par fonctionnalités, la découpe en bundles permet l'échange de bundles entre applications ! Cela signifie que vous pouvez développer une fonctionnalité, puis la partager avec d'autres développeurs ou encore la réutiliser dans un de vos autres projets. Et bien entendu, cela marche dans l'autre sens : vous pouvez installer dans votre projet des bundles qui ont été développés par d'autres !

Le principe même des bundles offre donc des possibilités infinies ! Imaginez le nombre de fonctionnalités classiques sur un site internet, que vous n'aurez plus à développer vous-mêmes. Vous avez besoin d'un livre d'or ? il existe sûrement un bundle. Vous avez besoin d'un blog ? il existe sûrement un bundle, etc.

Les bundles de la communauté

Presque tous les bundles de la communauté Symfony2 sont regroupés sur un même site : http://knpbundles.com/. Il en existe beaucoup, et pour n'en citer que quelques-uns :

  • FOSUserBundle : c'est un bundle destiné à gérer les utilisateurs de votre site. Concrètement, il fournit le modèle utilisateur ainsi que le contrôleur pour accomplir les actions de base (connexion, inscription, déconnexion, édition d'un utilisateur, etc.) et fournit aussi les vues qui vont avec. Bref, il suffit d'installer le bundle et de le personnaliser un peu pour obtenir un espace membre !
  • FOSCommentBundle : c'est un bundle destiné à gérer des commentaires. Concrètement, il fournit le modèle commentaire (ainsi que son contrôleur) pour ajouter, modifier et supprimer les commentaires. Les vues sont fournies avec, évidemment. Bref, en installant ce bundle, vous pourrez ajouter un fil de commentaires à n'importe quelle page de votre site !
  • GravatarBundle : c'est un bundle destiné à gérer les avatars depuis le service web Gravatar. Concrètement, il fournit une extension au moteur de templates pour pouvoir afficher facilement un avatar issu de Gravatar via une simple fonction qui s'avère être très pratique.
  • Etc.

Je vous conseille vivement de passer sur http://knpbundles.com/ avant de commencer à développer un bundle. S'il en existe déjà un et qu'il vous convient, il serait trop bête de réinventer la roue. ;) Bien sûr, il faut d'abord apprendre à installer un bundle externe, patience !

La structure d'un bundle

Un bundle contient tout : contrôleurs, vues, modèles, classes personnelles, etc. Bref, tout ce qu'il faut pour remplir la fonction du bundle. Évidemment, tout cela est organisé en dossiers afin que tout le monde s'y retrouve. Voici la structure d'un bundle à partir de son répertoire de base, vous pouvez en voir l'illustration grâce au bundle exemple fourni par défaut dans src/Acme/DemoBundle/ :

1
2
3
4
5
6
7
8
9
/Controller          | Contient vos contrôleurs
/DependencyInjection | Contient des informations sur votre bundle (chargement automatique de la configuration par exemple)
/Entity              | Contient vos modèles
/Form                | Contient vos éventuels formulaires
/Resources
-- /config             | Contient les fichiers de configuration de votre bundle (nous placerons les routes ici, par exemple)
-- /public             | Contient les fichiers publics de votre bundle : fichiers CSS et JavaScript, images, etc.
-- /views              | Contient les vues de notre bundle, les templates Twig
/Tests               | Contient vos éventuels tests unitaires et fonctionnels. Nous développerons sans faire de tests au début.

La structure est assez simple au final, retenez-la bien. Sachez qu'elle n'est pas du tout fixe, vous pouvez créer tous les dossiers que vous voulez pour mieux organiser votre code. Mais cette structure conventionnelle permet à d'autres développeurs de comprendre rapidement votre bundle. Bien entendu, je vous guiderai pour chaque création de fichier. ;)


En résumé

  • Symfony2 est organisé en quatre répertoires : app, src, vendor et web.
  • Le répertoire dans lequel on passera le plus de temps est src, il contient le code source de notre site.
  • Il existe deux environnements de travail :
    • L'environnement « prod » est destiné à vos visiteurs : il est rapide à exécuter, et ne divulgue pas les messages d'erreur.
    • L'environnement « dev » est destiné au développeur, c'est-à-dire vous : il est plus lent, mais offre plein d'informations utiles au développement.
  • Symfony2 utilise l'architecture MVC pour bien organiser les différentes parties du code source.
  • Un bundle est une brique de votre application : il contient tout ce qui concerne une fonctionnalité donnée. Cela permet de bien organiser les différentes parties de votre site.
  • Il existe des milliers de bundles développés par la communauté, pensez à vérifier qu'il n'existe pas déjà un bundle qui fait ce que vous souhaitez faire !