Architecture : Le Design Pattern MVC en PHP
Par François Lasselin le mardi, octobre 20 2009, 22:23 - Génie Logiciel - Lien permanent
On désigne par design pattern (patron de conception) une manière récurrente de résoudre un problème de programmation; généralement via la programmation orienté objet, mais pas toujours.
Au fil du temps,certaines conceptions sont devenues récurrentes face à certaines situations et elles ont été documentées, nommées et standardisées. Dans le domaine du web, le Design Pattern Modèle Vue Controleur (MVC) est l'un d'eux.Dans un projet, une démarche de génie logiciel pousse à définir l'architecture d'une application dans le respect des design pattern. L'architecture MVC cherche à séparer trois choses :
- la façon d'accéder aux données
- l'interface homme/machine: l'habillage, le design
- les traitements liés au métier/domaine de l'application
Soit le Modèle, les Vues et les Contrôleurs. Les contrôleurs permettent de répondre aux actions de l'utilisateur. Chaque contrôle est associé à une vue : cette dernière permet de présenter l'information retournée à l'utilisateur. Les données sont issues du modèle (la logique métier).

Les patterns ont une histoire et ne sont pas figés. Ainsi, le pattern MVC a évolué vers le MVC2. Dans l'architecture MVC 2, il n'existe plus qu'un seul et unique contrôleur réceptionnant toutes les requêtes clientes.
Le contrôleur unique devient le point d'entrée exclusif de l'application. Il devient alors très aisé de centraliser la gestion des accès, des droits, des statistiques ou de toute autre fonctionnalité transverse.
Concrètement, en quoi cela consiste t'il ? L'expression la plus simple d'une page affichant un listing de prix pourrait être la suivante.
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en_US" xml:lang=
"en_US">
<head>
<title>
Exemple
</title>
</head>
<body>
<?php
$db=mysql_connect($dbhost,$dbuser,$dbpass);
mysql_select_db($dbname,$db);
$query="SELECT product.name, product.price FROM product where product.type=42";
$req=mysql_query($query)or die('Erreur SQL!<br>'.$sql.'<br>'.mysql_error());
while ($row = mysql_fetch_array($req)) {
echo "Nom :".$row[0]." Prix :".$row[1];
}
mysql_close();
?>
</body>
</html>
Découpons le code. (Les sources sont téléchargeables sur la page des ressources)
D'abord, en construisant un aiguillage à requête.
Point d'entré unique : index.php:
if ($_GET['do']=="")
{
require ("default.html");
}
else
{
if($_GET['do']=="affichage")
{
require("action/class_example.php");
new Example();
}
}
?>
Controleur : /action/class_example.php:
require ("dao/dao_example_dao.php");
class Example
{
public function example()
{
$exampleDao= new ExampleDao();
$data = $exampleDao->get_products();
require("web/affichage.phtml");
}
}
?>
L'accès aux données est isolé dans une classe dao (Data access Object, autre design pattern)
Modèle : dao_example_dao.php
require ("class_connect_bd.php");
class ExampleDao
{
public function get_products()
{
$sql = "SELECT product.name, product.price FROM product where product.type=42";
$query = mysql_query($sql) or die('Erreur SQL !<br>'.mysql_error());
$all = mysql_fetch_all($query);
return $all;
}
}
?>
Le lecteur averti notera que la fonction mysql_fetch_all() n'est pas une fonction native de php. Il convient de l'implémenter. Ici dans le class_connect_bd.php:
$db = mysql_connect('localhost', 'user', 'password');
mysql_select_db('test',$db);
function mysql_fetch_all($query, $kind = 'assoc') {
$result = array();
$kind = $kind === 'assoc' ? $kind : 'row';
eval('while(@$r = mysql_fetch_'.$kind.'($query)) array_push($result, $r);');
return $result;
}
?>
Enfin, l'affichage:
Vue : /web/affichage.phtml:
<head>
<title>
Exemple
</title>
</head>
<body>
<?
foreach ($data AS $row )
{
?>
Nom :
<? echo $row["name"]; ?>
Prix :
<? echo $row["price"]; ?> <br/>
<?
}
?>
</body>
</html>
Évidement sur un exemple court, l'explosion du nombre de lignes du code n'est pas à l'avantage du pattern. Toutefois, sur le développement d'une application, le rangement opéré sur le code est nécessaire. En effet, le MVC apporte un niveau d'industrialisation du code.
Cette architecture est particulièrement préconisée dans la mise en œuvre d'une application Web pour de nombreuses raisons.
Le point d'entré unique permet la mise en place de traitement centralisé. Typiquement, le contrôle d'accès. Plutôt que de copier coller (et oublier...) un test sur chaque page pour vérifier que l'utilisateur est identifié. On réalise ce test une fois et ce code est maintenu à un seul endroit.
Le code est également beaucoup plus facile à maintenir.
- L'enchainement des écrans est défini par le point d'entré unique , une modification se fera à cet endroit (et pas en partant à la recherche des 90 liens perdus dans une montagne de code)
- Si demain le produit n'est plus à chercher en base de données mais dans un service web, il est facile de ne modifier que le dao correspondant sans impacter la logique métier.
- Et si le design évolue, seul les fichiers de la vue sont concernés.
Les bénéfices d'une bonne structuration d'un code sont nombreux. C'est pour cela que le MVC est devenu incontournable et qu'il est utilisé dans la quasi totalité des framework PHP.
La discussion continue ailleurs
URL de rétrolien : http://blog.nalis.fr/index.php?trackback/60

Commentaires
Merci pour ce billet simple mais clair qui présente de manière concise et précise ce qu'il faut savoir à ce sujet.
Le meilleur article pour débutant que j'ai pu trouvé à ce sujet.
Le principe MVC est très bien expliqué.
Merci !
Merci, c'est bien expliqué.
J'ai une question,
des fonctions du type "mysql_fetch_array" ne devrait pas figurer dans le code de vue.. ou je me trompe?
merci
@linka
C'est une bonne question.
Non, les fonctions du type "mysql_fetch_array" ne doivent pas être dans la vue. Comme son nom l'indique, "mysql_fetch_array" est une fonction spécifique au SGBD MySQL. Il faut donc l'isoler dans le modèle car cela est spécifique à la façon dont les données sont stockées.
C'est pourtant bel et bien le cas dans l'exemple :s
En effet, c'est une erreur. J'ai corrigé.
Il y a une explication à ça. Il n'y a pas de fonction fetch_all en mysql. Du coup, il est tentant de parcourir les résultats au moment de l'affichage.
Mais il n'y avait donc pas isolation du traitement des données.
Pour rectifier le tir, j'ai inclus une fonction mysql_fetch_all().
Les sources du script sont maintenant téléchargeables.
"action/class_example.php/" fait partit du modèle ou du contrôleur? Si il fait partit du modèle, "require("web/affichage.phtml");" ne devrais pas plutôt être dans le contrôleur juste après "new Example();" pour que se soit le contrôleur qui s'occupe de choisir la vue et non le modèle?
Bonjour Guillaume,
Non, class_example.php c'est le contrôleur. L'appel à la vue est donc bien justifié ici.
Pour plus de clarté, J'ai ajouté "Modele", "Vue", "Controleur" et "Point d'entré unique" avant haque fichier correspondant.
Hum... mais le point d'entré unique me semble être un contrôleur dans ton cas... puisque en théorie c'est le contrôleur qui reçoit la requête et choisi le modèle et la vue qui sera utilisé (L'aiguillage fait partie intégral du contrôleur). Hors, dans ton point d'entré, tu effectue un choix entre affiché default.html (donc afficher une vue) ou de créé un "exemple" qui va appeler un modèle et afficher une autre vue. C'est, il me semble, clairement la fonction d'un contrôleur. Ton index.php correspond au contrôleur du pattern MVC2. Ton /action/class_example.php est une action donc possiblement un espèce de sous-contrôleur OU, en déplaçant l'appel à la vue dans index.php, faire partit du modèle. J'avoue qu'il y a pour moi une partie flou entre où doit finir le contrôleur et commencer le modèle. Par contre, je suis certains que ton index.php EST un contrôleur. À bientôt!
Le point d'entrée unique est ce que l'on appel un Front Controller, il fait le premier aiguillage vers les différents modules de ton application. Ensuite tu a tes Back Controller classiques. Le premier appelant les seconds. Tu rencontre assez souvent cette architecture.
Votre explication du principe MVC est très compréhensible ce qui est assez rare pour le souligner. Ce design pattern est important pour créer des applications propres, lisibles et donc plus facilement maintenable (ce que certains développeurs ont tendance à oublier). A mon sens, c'est un principe fondamentale pour un développeur.
Bravo pour cet article clair
Enfin un moyen simple et efficace de faire comprendre le fonctionnement du MVC. Tout simplement merci et bravo a Francois Lasselin pour se billet.
/clap