Table des matières
J'ai discuté avec bon nombre de développeurs ces dernières années, et la première inquiétude qu'ils exprimaient à propos de l'adoption d'un Framework de développement d'application Web est la compréhension de l'architecture de ces Frameworks. La quasi-totalité des Frameworks conçus pour le Web ont adopté une architecture décrite pour la première fois en 1979 par Trygve Reenskaug, nommée Modèle-Vue-Contrôleur, ou MVC (En anglais : Model-View-Controller). Cela semble être une vieille idée, mais sa récente adoption par les applications Web semble lui aller comme un gant ; plus même que ça n'a jamais été le cas pour les applications de bureau !
En PHP, il y a beaucoup de confusion autour de l'idée de MVC. Beaucoup de développeurs la perçoivent comme étant complexe, difficile à prendre en main, et ne valant pas le coup. Ce sont ces personnes qui accusent souvent le MVC de consommer du temps, de nuire aux performances, ou d'être encombré de fonctionnalités et de code source inutiles. Même si je comprends ces sentiments, ils ne sont pas entièrement vrais, puisque le MVC est réellement une idée plutôt simple. En fait, l'architecture MVC est maintenant vue comme une des raisons principales de la grande popularité des Frameworks Web.
Puisqu'il est à peu pès impossible de comprendre Zend Framework sans avoir quelques notions à propos du MVC, il faut l'expliquer avant de commencer : ceci n'est pas un chapitre que vous voulez sauter ! La première chose qu'il faut vous souvenir est que le terme MVC ne désigne pas une implémentation spécifique ; c'est plutôt un terme qui défini un modèle de fonctionnement, dont l'implémentation varie en fonction des objectifs de chaque Framework, de l'application, et du langage de programmation utilisé. Ce principe d'une solution générale répondant à un problème de programmation, où l'implémentation n'est pas spécifique, mais détaillée en des termes généraux, est connu sous le nom de "Design Pattern". Nous verrons plus d'exemples de comment est-ce que les Design Patterns permettent de comprendre l'architecture du Framework dans les chapitres suivants.
Pour rappel, repensons, l'espace d'un instant, à l'historique de l'utilisation de PHP pour le développement de pages Web dynamiques... L'approche typique est souvent désignée sous le terme de "Page Controller" (il y a aussi l'idée de "Transaction Script", mais il est impossible d'expliquer la fiabilité du chaos total !). Page Controller est un autre Design Pattern décrit par Martin Fowler dans son célèbre livre "Patterns Of Enterprise Application Architecture" (fréquemment abrégé en POEAA au sein de la communauté). Il décrit une approche où chaque page HTML de votre application peut avoir son propre fichier PHP dédié. Souvent, ce principe se termine avec un fichier HTML par page PHP, mais seulement si ces pages sont suffisamment proche (par exemple, les formulaires et leur gestions sont fréquemment semblables) pour que les relations se forment de la nécessité de réutiliser du code source dans le même fichier. En général, ces pages auront des inclusions communes en début de fichier, pour gérer les imports de bibliothèques et/ou de constantes. Par exemple, toutes les pages peuvent avoir besoin de Smarty, ou d'une connexion à la Base de Données.
Cela dit, dans ce scénario, il devient assez difficile de géréer les évolutions et changements. Chaque nouvelle fonctionnalité ou correction de bug va nécessiter du code supplémentaire, et où vous allez finir par mettre ce code va devenir un facteur majeur de la maintenabilité de votre application. Peut-être qu'une modification mineure demandera en fait d'apporter plusieurs changements à de multiples portions de logique métier, qui, au fur et à mesure du temps qui passent, auront été disséminées à travers des dizaines de fichiers ; et, en conséquence, vous finirez par dupliquer des sections entières de code source. Peut-être découvrirez-vous que des requêtes SQL dans une autre dizaine de fichiers font toutes référence à une table dont vous êtes en train de modifier le nom... Vous pouvez imaginer que la quantité de petites modifications telles que celle-ci, dans de multiples fichiers, va litéralement finir par exploser, et vous finirez par réaliser que le simpleme fait d'essayer de maintenir cette masse de code source demande en fait une énorme quantité de temps et d'énergie. C'est à ce moment que beaucoup de projets cessent de vivre, et ne font plus que stagner, malgrès l'enthousiame de leurs développeurs. Je suis moi-même tombé dans ce piège, et en être témoin aux premières loges est ce qui m'a encouragé à rechercher une meilleure approche.
Au fil des années, le développement d'applications en PHP a vécu plusieurs révolutions importantes. La plus importe de toutes a été la large adoption de la Programmation Orientée Objet (POO ; en anglais Object Oriented Programming, ou POO). Une deuxième a été la mise en application, lente mais sûre, de bonnes pratiques permises par la POO, comme les Tests Unitaires Automatisés. Enfin, la troisième a été l'explosion de l'utilisation du Design Pattern d'architecture qu'est le MVC, et son influence sur la génération actuelle de Frameworks de développement Web, comme Zend Framework.
L'architecture Modèle-Vue-Contrôleur (généralement abrégée en "MVC") est une solution générale, ou un Design Pattern Applicatif, à la question de la séparation des responsabilités dans une application de manière fortement structurée. Globalement, ça permet d'éviter le chaos total.
Son objectif est d'empécher des portions de code très peu similaires de se mélanger abusivement, en mettant l'accent sur le fait qu'il y a trois types de composants qui devraient communiquer à une certaine distance, par le biais de quelques interfaces. Bien que cela ait l'air horriblement compliqué (ne vous inquiétez pas !), cela empéche le code source de muter sauvagement en un mélange spaghetti, si souvent caractéristique d'applications PHP désorganisées.
Le nom de cette architecture mentionne les trois composants issus de la séparation : le Modèle, la Vue, et le Contrôleur. Bien que le MVC puisse sembler, aux yeux de certains, faire parti de ces concepts informatiques obscurs, c'est en fait, réellement, un concept des plus simples.
Vous prenez un bout d'une fonctionnalité, déterminez sa raison d'être, l'assignez à l'une des trois entités issues de la séparation, et finalement, vous la rattachez à une classe spécifique au sein de cette couche. Une fois que tout est découpé correctement, vous vous retrouvez avec une application composée de petites pièces qui sont réutilisables, centralisées, accessibles, et s'embriquent les unes avec les autres comme des blocs de construction exposant une API abstraite pour permettre de les lier les uns aux autres ; et travailler avec des API abstraites rend les changements incrémentaux extrêmement simples (si ça a été fait correctement, bien entendu !). Une fois que tout a été organisé sous forme d'objets ayant chacun des responsabilités spécifiques, le coût de changement est normalement réduit de manière significative... Ce qui est, après tout, l'objectif général : nous voulons qu'apporter des modifications soit peu coûteux, facile, et ne nous effraie pas.
De toute évidence, je ne couvrirai pas l'intégralité du domaine de la Programmation Orientée Objet ici. Mais j'espère que le message est suffisament clair. L'approche MVC renforce tous les bénéfices que de bonnes pratiques de POO devraient apporter. Le code adhère à des principes tels que Don't Repeat Yourself (DRY - ne vous répétez pas) et Keep It Simple Stupid (KISS - Gardez le stupidement simple), qui découragent la duplication bordélique, et améliorent la réutilisation et la maintenabilité.
Une chose qui a souvent tendance à perdre un peu les développeurs, cela dit, est que l'approche MVC semble générer plus de code que si vous ne l'utilisez pas du tout. Cela ne signifie nullement que l'approche MVC soit mauvaise, c'est juste un effet fréquent de la Programmation Orientée Objet, et une conséquence bien identifiée d'une plus grande structuration du code. Lorsque vous utilisez un Framework d'application Web, ce problème n'est pas le votre : vous n'écrivez pas le Framework à partir de zéro, n'est-ce pas ? Une autre raison de l'augmentation de la quantité de code est relativement évidente : les Frameworks ajoutent une multitude de fonctionnalités qui vont plus loin que ce qu'une implémentation MVC basique offrirait.
Puisque la POO entraine la création d'un grand nombre de classes, vous devriez comprendre pourquoi est-ce que c'est une bonne chose. Un script massivement procédural aurait contenu moins de code, et aurait même été plus léger ; mais ce n'est pas une solution à long terme. Le code orienté objet est conçu de manière à diminuer de manière significative les coûts de maintenance, tests, adaptation, et réutilisation. Ceux-ci ont normalement tendance à dépasser, et de loin, le seul coût de développement ! Rappelez-vous que vous allez devoir vivre avec ce code de folie pour des années : écrivez une fois, utilisez pour toujours. Recréer des solutions n'est pas une méthode de développement viable, puisqu'au lieu d'avoir un coût immédiat de mise en route que vous ne reverrez plus jamais, vous aurez des coûts constamment croissants à chaque réinvention de la roue sous une forme légérement différente. Ré-écrire ou ré-inventer est coûteux, et, en plus de cela, risqué.
Zend Framework, en sa
qualité de Framework "glue", propose une implémentation MVC sous forme de
composants. Cela signifie que l'architecture MVC du Framework est découpée
en plusieurs composants à première vue indépendants, dont
Zend_Controller
, Zend_Db
si
le composant fait parti d'un Modèle, Zend_View
, et
Zend_Layout
. Chaque composant assume une tâche
particulière de l'approche MVC, mais conserve toutes les caractéristiques
de composants indépendants, que vous pouvez utiliser en dehors d'une
application architecturée selon une approche MVC.
Tout ceci résulte sans aucun doute en une profusion de classes spécialisées, au point qu'il peut devenir difficile de comprendre comment toutes les pièces s'imbriquent, mais, honnêtement, vous n'avez besoin que de l'API abstraite externe de chaque composant, et pouvez ignorer le reste, à moins que vous ne vouliez réellement personnaliser quelque chose. Pour chaque méthode communément utilisée, il y en a probablement cinq autres qui n'intéresseront qu'une minorité de développeurs. Ceci met en évidence l'adaptabilité de Zend Framework : les méthodes supplémentaires existent pour que vous puissiez personnaliser le Framework si le besoin s'en fait sentir (et c'est quasiment toujours le cas, à moins que vous n'aimiez les trous de pigeons peu spacieux).
Laissez-moi insister sur
un point afin d'effacer tout doute qui subsisterait : l'approche MVC est
aussi répandue que la poussière. Quasiment tous les Framework Web modernes
utilisent le MVC. C'est utilisé par Zend Framework, Symfony
, Django
pour Python,
Spring, Ruby On Rails
et
Merb
. Tirez un Framework au
hasard, et, statistiquement, il est probable qu'il se décrive comme un
Framework MVC !
Maintenant, voyons chaque composant du MVC un peu plus en détails !
La Vue est responsable de la génération de l'interface utilisateur de votre application. En PHP, c'est souvent défini (de façon un peu restrictive) comme la couche au sein de laquelle vous placez tout votre HTML de présentation. Bien que ce soit vrai, c'est aussi l'endroit où vous pouvez créer un mécanisme de génération dynamique de HTML, RSS, XML, JSON, ou n'importe quoi qui soit envoyé à l'application cliente, que ce soit un navigateur, ou quelque requête du genre web service. Ce type de génération prend généralement en entrée une ou plusieurs classes Modèle comme source des données dynamiques que la Vue est chargée de représenter. Cela dit, nous ne pouvons pas être trop stricts : la Vue peut aussi recevoir des données simples, comme des scalaires et des tableaux, donc ne commençons pas à envelopper chaque morceau de donnée dans une classe Modèle si ce n'est pas nécessaire.
La Vue est généralement organisée en fichiers de templates, mais elle peut aussi être simplement affichée depuis, ou manipulée par, le Contrôleur avant sa sortie. Il est essentiel de se souvenir que la Vue n'est pas uniquement un format de fichier : elle inclut aussi tout le code PHP ou les tags analysés pour organiser, filtrer, décorer, et manipuler le format, à partir des données reçues depuis un ou plusieurs Modèles (ou, comme c'est souvent le cas, passées depuis le Modèle à la Vue par le Contrôleur).
Un second aspect de la Vue est que, puisque tout le contenu est généré dynamiquement par PHP, il est tout à fait raisonnable de créer des plugins personnalisés, au sein desquels factoriser des tâches fréquemment exécutées. Par exemple, lorsque nous traduisons le titre d'un article de blog en un format adapté à une URL, il est nécessaire de supprimer certains caractères de ponctuation, et de remplacer les caractères non-ASCII par leurs équivalents ASCII. Cette tâche pourrait facilement devenir un plugin de Vue, qui opèrerait sur le texte du titre de l'entrée lorsque nous ajoutons des URLs à nos templates. Nous savons que de tels plugins font parti de la Vue, puisqu'ils correspondent à de la logique de présentation - logique dont le seul objectif est de manipuler la présentation des données.
Notons au passage que ce livre d'utilisera pas Smarty ou toute autre bibliothèque indépendante pour interpréter les templates. Smarty a une histoire respectée en PHP, mais souffre de manques sérieux à partir du moment où vous commencez à considérer la Vue comme un Puzzle pouvant regrouper jusqu'à des dizaines de pièces réutilisables, jointes les unes autres autres pour former une structure complexe. En fait, cette méthode de mise en place de Vues est tellement proche de, et liée à, la programmation objet qu'il devient presque inévitable d'utiliser PHP lui-même comme langage de templating. Bien entendu, cela a un coût, puisque bon nombre de designers web ne connaissent pas PHP ; mais, au besoin, Zend Framework vous permet d'intégrer n'importe quel moteur de template que vous préféreriez utiliser.
Les Contrôleurs sont presque trompeusement simples en comparaison de la Vue et du Modèle. La première fonction du Contrôleur est de contrôler et de déléguer. Au cours d'une requête typique vers une architecture MVC appliquée au Web, le Contrôleur va récupérer les données entrées par l'utilisateur, les traduire en actions pour un ou plusieurs Modèles, et retourner la sortie générée par la Vue à partir des résultats des actions de ces classes Modèles.
Maintenant, je vais immédiatement insister sur le fait que cette logique met en évidence un point important à propos des Contrôleurs. Les Contrôleurs déléguent autant que possible, parce que si vous les surchargez de responsabilités, il deviendront rapidement énormes, et difficile à maintenir. En fait, ils commenceront à ressembler dangereusement à la vieille approche du Page Controller, ce qui serait MAL(TM). La logique applicative devrait être déléguée aux Modèles, autant que faire se peut. Nous allons étudier les Modèles très bientôt, et nous entrerons plus dans le sujet des Modèles au Chapitre 3.
Le Contrôleur a aussi une spécificité par rapport aux autres formes d'architectures PHP : il ne requiert qu'un seul point d'entrée pour l'application - quasiment toujours appelé index.php. L'existence d'une seule route vers l'application rend certaines tâches nettement plus simples. Par exemple, vous pouvez définir une chaîne de filtres de requête qui s'exécutent sur toutes les requêtes. Les Access Control Lists (ACL - Listes de Contrôle d'Accès) sont un excellent exemple d'un type de filtre des plus répandus. Brancher un firewall applicatif ou un honey pot (couches de sécurité) en est un autre. D'ailleurs, nous verrons un peu plus loin, dans un autre chapitre, un projet trop peu connu, nommé PHP-Intrusion Detection System (PHPIDS - System de Détection d'Intrusion en PHP).
Le Modèle peut être présenté de plusieurs manières différentes. En fait, vous pouvez écrire des livres entiers juste à propos du Modèle - et pas mal de gens l'ont fait ! Le Chapitre 3 entre plus en détails dans ce domaine, mais parlons en rapidement ici.
Le Modèle a deux rôles largement définis :
1. Le modèle est reponsable de la conservation de l'état entre les requêtes HTTP.
Pour faire simple, n'importe quelle donnée, que ce soit en Base de Données, dans un fichier, stockée en Session, ou cachée via APC, doit être préservée entre les requêtes, et fait donc parti de l'état de l'application au moment où la dernière requête a été terminée. Donc, souvenez-vous que le Modèle ne désigne pas uniquement la Base de Données. Même les données que vous obtenez via des appels Web Services peut être représentées comme un Modèle ! Oui, même des flux RSS ! Les Frameworks qui présentent trop rapidement des introductions au Modèle ont tendance à ne jamais expliquer cela clairement, ce qui ne fait qu'accroitre les malentendus.
Voici un exemple :
Zend_Feed_Reader
, que j'ai développé avec Jurriën
Stutterheim, est en fait un Modèle. Il lit des flux, les manipule,
interprête des données, ajoute des restrictions et des règles, et créée
généralement une représentation utilisable des données sous-jacentes.
Sans lui, vous avez Zend_Feed
, qui requiert
beaucoup de travail supplémentaire pour le développer en un Modèle
viable. En d'autres mots, là où Zend_Feed_Reader
est un Modèle partiel, Zend_Feed
se limite au pur
accès aux données.
2. Le Modèle incorpore toutes les règles, contraintes, et comportements gouvernant et utilisant cette information.
Par exemple, si vous écriviez du code de logique métier pour un Modèle Commande dans une application de gestion de stock, les règles internes de la société pourraient définir que les commandes soient soumises à une limite unitaire de 500 € en liquide. Les achats de plus de 500 € devraient être considérés comme des actions illégales par votre Modèle Commande (sauf, peut-être, si autorisées par quelqu'un d'important au sein de la société). Le Modèle devrait être capable de forcer le respect de cette contrainte.
Cela prend tout son sens si vous réfléchissez à la signification du mot "Modèle". Nous avons des modèles de climats en climatologie, par exemple, qui définissent les données, exécutent des processus, assument des comportements, et calculent des résultats probables à partir de tout cela. Le M de MVC s'appelle Modèle pour une raison. Le Modèle ne représente pas uniquement des données, mais tout le système pour lequel cette donnée est utile. Bien entendu, n'importe quel Modèle peut être suffisamment compliqué pour nécessiter plusieurs Modèles interagissant les uns avec les autres... Mais vous avez saisi l'idée.
Si vous avez lu ces deux points, vous réalisez peut-être quelque chose d'intéressant. A l'exception de l'interface utilisateur, la plupart de l'application peut être exprimée au sein des Modèles. C'est le Modèle qui regroupe toutes les données et les comportements correspondant à celles-ci, et parfois même comment présenter celles-ci. C'est le Modèle qui peut comprendre, interpréter, et représenter ces données, et lui donne des usages pratiques. C'est aussi un endroit logique où placer les règles de validation et les contraintes : pourquoi ne pas les conserver avec les données sur lesquelles elles travaillent ?
Ces observations
rendent évidentes le fait que le Modèle est la partie du trio MVC que
vous devez développer quasiment à partir de zéro. Zend Framework peut
vous fournir des composants d'accès aux données, mais il ne peut pas
prédire ce que vos applications vont faire ! Les Modèles allant bien
plus loin que le simple accès aux données, il reste beaucoup de points
qui doivent être développés et testés avant que nous n'ayez un Modèle
viable. Ce n'est pas toujours aussi simple que d'étendre
Zend_Db_Table
.
Nous étudierons le Modèle en détails au Chapitre 3, puisqu'il y a une énorme quantité d'informations à prendre en compte à propos de ce qu'il représente, et de la place qu'il occupe au sein de l'architecture MVC toute entière.
L'architecture Modèle-Vue-Contrôleur est devenue une solution largement reconnue comme adaptée aux applications Web, et c'est évident au sein de la majorité des Frameworks de la génération actuelle, pour la plupart des langages de programmation.
Avec Zend Framework, ces
trois séparations sont représentées par les composants
Zend_Db
, Zend_View
,
Zend_Layout
, et
Zend_Controller
. Vous entendrez beaucoup parler de
ces quatre composants du Framework, et des classes qui les composent, dans
les chapitres à venir ! Ensemble, ils forment la base de l'architecture
MVC du Zend Framework, et regroupent beaucoup de ses bonnes
pratiques.
L'approche MVC était, au départ, utilisée pour mettre en avant la "séparation des rôles" au sein des applications graphiques de bureau. En séparant chaque rôle dans une couche indépendante de l'application, cela diminuait les liens en interdépendances, ce qui, à son tour, rendait les applications plus faciles à concevoir, écrire, tester, et maintenir. Bien que les applications GUI aient tourné le dos au MVC ces dernières années, c'est une approche qui a prouvée être extrêmement efficace une fois appliquée aux applications Web.
Dans le Framework, cela
ajout beaucoup de structure prévisible, puisque chaque segment du MVC pour
n'importe quelle requête est limité à son propre groupe de fichiers. Le
Contrôleur est représenté par Zend_Controller_Front
et les sous-classes des Zend_Controller_Action
, le
Modèle par des classes domaines ou d'autres types de classes qui peuvent
être basées sur Zend_Db_Table
et
Zend_Db_Row
pour ce qui est de la persistance de
Base de Données, et la Vue par les fichiers templates .phtml et les
Helpers de Views. Zend Framework se charge de déterminer comment chacun
est organisé au sein du tout, vous laissant libre de vous concentrer
uniquement sur ces regroupements, sans avoir à vous soucier du code les
combinant entre eux en une unité de travail cohérente.
D'une certaine manière, c'est comme construire une maison où les fondations, les murs, le câblage interne, et la plomberie sont déjà en place, et que tout ce qu'il reste à faire est la décoration intérieure et le toit. Ca prendra peut-être un peu de temps pour aprendre à décorer et couvrir les sections déjà préparées, mais une fois que vous aurez appris comment faire, les maisons suivantes seront terminées beaucoup plus vite !
Ce chapitre constituait une introduction rapide au concept d'architecture Modèle-Vue-Contrôleur (MVC). Malgré sa longueur, ce n'est pas un examen exhaustif, mais il couvrait suffisament de notions pour que les concepts cités ultérieurement ne soient pas totalement hors de propos et dépourvus de contexte. En dehors de ce livre, il existe une quantité phénoménale d'articles à propos des moindres détails de l'architecture MVC, que ce soit sur Internet ou non, et je vous encourage à aller en parcourir quelques uns, car ce ne ne sera pas du temps perdu. Chaque élément que vous comprendrez vaut le temps vous y passerez !
Au prochain chapitre de ce voyage, nous passerons quelque temps à faire connaissance plus en profondeur avec le Modèle. Ce point est traité séparément, car le Modèle est un concept qui peut poser beaucoup de difficultés, et a de nombreux usages.