Model View Controller (MVC) - Architecture des applications PHP - PHP - Programmation
Marsh Posté le 20-09-2005 à 11:20:18
Je suis en train d'essayer de faire un truc vraiment propre, dans ce genre...actuellement c'est pas très très loin, sauf que le controleur est le script...
Marsh Posté le 20-09-2005 à 11:21:07
ReplyMarsh Posté le 20-09-2005 à 11:22:17
FlorentG a écrit : Comment ça, "le controleur est le script" ? |
Ben pour l'instant j'ai pas d'objet(s) controleur(s)...
C'est le script qui joue ce rôle, donc...
Marsh Posté le 20-09-2005 à 11:24:07
Ah ok
Moi je réfléchis à peut-près comment le structurer... La version switch massive est assez à chier Surtout le plus dur, c'est de se fixer si telle ou telle couche doit être ultra générique, ou si tu dois coder pleins de trucs en dur... L'idéal serait que les trois soit entièrement configurables via fichier de config...
Marsh Posté le 20-09-2005 à 11:29:09
UserModel, UserListView
miserable failure
Marsh Posté le 20-09-2005 à 11:32:39
ReplyMarsh Posté le 20-09-2005 à 11:36:06
nraynaud a écrit : UserModel, UserListView |
Hésite pas aussi à faire tes remarques, en prenant soin de compléter, de corriger, d'argumenter, d'expliquer
Marsh Posté le 20-09-2005 à 11:38:44
FlorentG a écrit : C'est un exemple bidon et vite-fait |
bah oui, mais ça montre que tu as raté un truc.
si le modèle c'est un utilisateur, la vue ça serait une fiche résumée de l'utilisateur (style la page des préférences sur HFR), les opérations ça serait style changer la date de naissance, changer la photo.
Le contrôleur, c'est assez technique mais ça serait le truc qui interprête le formulaire lors du submit et feraient les actions de modification.
si le modèle c'est une liste, alors la vue est un tableau, et les actions sont bien ajouter, supprimer.
Et évite de sufixer les modèles de "Model", un utilisateur c'est un utilisateur, une liste est une liste, c'est tout.
Marsh Posté le 20-09-2005 à 11:43:19
nraynaud a écrit : bah oui, mais ça montre que tu as raté un truc. |
Nonon, c'est un exemple simple
nraynaud a écrit : si le modèle c'est un utilisateur, la vue ça serait une fiche résumée de l'utilisateur (style la page des préférences sur HFR), les opérations ça serait style changer la date de naissance, changer la photo. |
Oui C'est un exemple que j'ai vu un peu partout, on mix un seul utilisateur et toute la liste. Pour expliquer plus simplement, c'est le mieux. Sinon faut déjà deux objets, un "UserModel", et un "UserListModel" (hmmm, suffixes ).
nraynaud a écrit : Le contrôleur, c'est assez technique mais ça serait le truc qui interprête le formulaire lors du submit et feraient les actions de modification. |
Voilà, dans mon exemple y'a un cas 'add' où on ajoute un User.
nraynaud a écrit : Et évite de sufixer les modèles de "Model", un utilisateur c'est un utilisateur, une liste est une liste, c'est tout. |
C'est pour pas perdre les débutants, comme ça ils repèrent bien les couches. Sinon je ne suffixe presque jamais les nom de classes
Marsh Posté le 20-09-2005 à 11:45:40
Pour ces histoire d'User et d'UserList, y'a la table Data Gateway qui peut être utilisée.
Marsh Posté le 20-09-2005 à 12:08:15
aussi. J'pensais faire qqchose comme ça pour un futur site, c'est donc bienvenu.
Marsh Posté le 21-09-2005 à 07:31:44
ReplyMarsh Posté le 21-09-2005 à 08:00:14
ReplyMarsh Posté le 21-09-2005 à 08:02:47
uriel a écrit : il est beau le bac+5 |
Sur le bac +5 j'en ai 4 en chmie-biologie !
Marsh Posté le 21-09-2005 à 09:08:04
Sympa, je vais peut-etre finalement arriver à comprendre ce système plus que survolé en licence ^^'
Merci FlorentG
Marsh Posté le 21-09-2005 à 09:18:11
le livre php5 de chez Eyrolles abordait le MVC, apres je sais pas ce qu'il vaut, mais cette collection 'jaune et rose' m'a semblé bien pour debuter.
Marsh Posté le 21-09-2005 à 09:23:27
Y'a aussi le Php|architect de Mai 2003 qui en parle, et est disponible gratuitement (faut juste s'inscrire)
Marsh Posté le 21-09-2005 à 09:49:54
En fait j'ai toujours du mal à voir le réel intérêt du contrôleur, dans le cas qui nous occupe...si on implémente une classe dédiée, il ne reste plus rien à faire dans le script 'index.php' à part instancier la classe...
Marsh Posté le 21-09-2005 à 09:53:54
Bon, on va essayer de voir la partie "model" en détail.
Comme dit plus haut, le modèle encapsule l'accès aux données et la logique métier. On va y retrouver tout ce qu'il faut pour, par exemple, ajouter des éléments, en modifier, en supprimer, en récupérer, faire des calculs dessus, etc. Le modèle ne sait pas qu'il est utilisé dans un MVC : il n'utilisera jamais une vue ni un contrôleur. Ce sont ces deux derniers qui vont utiliser le modèle.
Niveau implémentation, plusieurs choix sont possibles... C'est ce qu'il faudrait discuter maintenant
Déjà se pose le problème de l'abstraction. Dans le tout premier exemple, j'ai un "UserModel". Faut-il avoir des modèles bien précis (ici on ajoute un utilisateur et rien d'autre), ou des modèles plus abstrait ? On aurait, à la place de UserModel, un simple ElementModel (terme mal choisi), avec des méthode "add", "edit", "delete". La méthode add prendrait pour paramètre un tableau associatif "nom de champ"->"valeur"...
Ensuite y'a l'accès à la source de données. Abstraction totale ? Ce qui est difficile à implémenter (voir mon premier diagramme UML, j'ai des DAO spécifiques pour différents cas : liste simple, ordonnée, hiérarchisée). Ou alors lier un modèle à sa source de données (on aurait des instructions MySql direct dans le modèle).
Qu'en pensez-vous : faut-il une abstraction ultime ? Ou alors garder simplement une abstraction vis-à-vis de la source de données, et des modèles un peu plus fixés ?
Marsh Posté le 21-09-2005 à 09:55:40
skeye a écrit : En fait j'ai toujours du mal à voir le réel intérêt du contrôleur, dans le cas qui nous occupe...si on implémente une classe dédiée, il ne reste plus rien à faire dans le script 'index.php' à part instancier la classe... |
Ouais, ça c'est une autre histoire, entre les FrontController, les PageController, les FormController, etc... Chacun a son utilisation... On peut avoir un FrontController, où toutes les requêtes vont passer, qui va instancier le bon modèle et la bonne vue... Ou alors chaque page possède son propre contrôleur (NewsController, ArticleController, GuestbookController...). On verra ça plus tard, là on va déjà examiner le model
Marsh Posté le 21-09-2005 à 09:56:28
FlorentG a écrit : Qu'en pensez-vous : faut-il une abstraction ultime ? Ou alors garder simplement une abstraction vis-à-vis de la source de données, et des modèles un peu plus fixés ? |
Perso j'ai directement les accès à la base dans le modèle...c'est pas très beau, mais il y a un moment où il faut arrêter de découper, je crois...
Marsh Posté le 21-09-2005 à 10:02:47
D'ailleurs, j'ai une question à propos des bases de données...Comment faire apparaitre la connexion à la base proprement dans tout ça?
Sachant que j'utilise oracle, et que j'ai donc obligatoirement un objet qui se ballade...
Actuellement je suis obligé de passer en paramètre cet objet à toutes les fonctions qui peuvent avoir besoin d'accéder à la base de données, c'est très moche...
Une autre solution serait de le référencer dans tous mes objets, mais ça me plait pas des masses non plus...
Marsh Posté le 21-09-2005 à 10:50:17
J'ai ça aussi pour MySql : un objet chargé de garder la connexion et de faire les requêtes.
Alors comme il doit être accessible un peu partout, j'ai utilisé un Singleton, avec des méthodes statiques permettant de faire les requêtes.
Marsh Posté le 21-09-2005 à 11:06:27
FlorentG a écrit : J'ai ça aussi pour MySql : un objet chargé de garder la connexion et de faire les requêtes. |
ah oui, pas bête.
Marsh Posté le 21-09-2005 à 11:13:44
skeye a écrit : En fait j'ai toujours du mal à voir le réel intérêt du contrôleur, dans le cas qui nous occupe...si on implémente une classe dédiée, il ne reste plus rien à faire dans le script 'index.php' à part instancier la classe... |
le controleurs, globalement, servent à rien et portent mal leur nom.
La partie importante c'est qu'on s'épare l'aspect visuel de l'aspect business.
Le contrôleur est entièrement dépendant de la vue et il est inenvisageable de le changer pour une vue donnée (je vois pas ce que la case "date de naissance" irait remplir de différent que le champs date de naissance du modèle). C'est essentiellement le listener des évènements des widgets qui va répercuter les actions sur le modèle : un rôle bien faible.
Marsh Posté le 21-09-2005 à 11:17:00
skeye a écrit : Perso j'ai directement les accès à la base dans le modèle...c'est pas très beau, mais il y a un moment où il faut arrêter de découper, je crois...:o |
Alors c'est plus du MVC.
Dans la logique MVC, la partie qui gére l'affichage se contre fiche de la provenance des données.
Elle se contente d'afficher ce qu'on lui indique.
En clair, elle ne va jamais chercher d'elle même les données à afficher.
Aprés, il y en a qui rendent le gestionnaire de données indépendant de la partie graphique pour pouvoir changer de support à tout moment, mais moi j'ai pas encore fait ça, a moins de l'avoir fait sans le savoir.
Marsh Posté le 21-09-2005 à 11:17:11
nraynaud a écrit : le controleurs, globalement, servent à rien et portent mal leur nom. |
C'est bien ce qu'il me semble...ça parait bcp de boulot pour pas grand chose de créer des classes spécifiques plutôt que faire ça directement dans un script bête et méchant...
Marsh Posté le 21-09-2005 à 11:18:24
omega2 a écrit : Alors c'est plus du MVC. |
Euh oui, et?
J'accède à la base dans le modèle, pas dans la vue ni dans le pseudo-contrôleur.
Marsh Posté le 21-09-2005 à 11:20:11
nraynaud a écrit : le controleurs, globalement, servent à rien et portent mal leur nom. |
Voilà Dans le web, c'est lui regardera l'URL, et s'occupera de réagir en conséquence : opération sur le modèle, et instanciation de la bonne vue. C'est pour ça que pas mal d'appli webs MVC utilisent un seul contrôleur pour toute l'appli (FrontController).
Bon moi je pense avoir plusieurs contrôleurs.
Ca sert plus de chef d'orchestre qu'autre chose
Marsh Posté le 21-09-2005 à 11:21:11
omega2 a écrit : Alors c'est plus du MVC. |
Si, parce que le MVC ne définit pas vraiment comment se structure la partie "model". On peut très bien avoir des requêtes SQL directes dans le modèles, ce n'est pas une violation du MVC...
Marsh Posté le 21-09-2005 à 11:24:16
nraynaud a écrit : le controleurs, globalement, servent à rien et portent mal leur nom. |
Pour mon site, j'ai un controleur qui regarde quelle page de quelle section est demandé afin de savoir quel objet instancier et quel traitement de l'objet déclancher. Du coup, mon controleur, réagit de la même maniére qu'on lui demande de rajouter des sections où qu'on lui demande d'afficher la page d'acceuil du site. C'est jsute qu'il déclanche pas les même actions derriéres.
Mais c'est vrai que mon controleur ne fait pas grand chôse par rapport au reste du systéme, mais le role du controleur n'est il pas juste de réagir aux actions de l'utilisateurs?
Marsh Posté le 21-09-2005 à 11:25:15
skeye a écrit : D'ailleurs, j'ai une question à propos des bases de données...Comment faire apparaitre la connexion à la base proprement dans tout ça? |
j'ai toujours eu un problème avec cet aspect, en particulier pour la démarcation des transactions.
En pratique dans le monde du web, tu peux faire un pire cas avec acquisition de la connection+démarrage de transaction à la réception de la requette, et relachement + commit au moment du renvoit de la réponse.
Si tu veux faire des choses plus fines (ce que j'espère), il te faudra un objet représentant le TransactionManager, et il stockera la connection lui-même et te le masquera dans l'objet transaction qu'il te donnera au début de transaction.
Marsh Posté le 21-09-2005 à 11:26:10
omega2 a écrit : Mais c'est vrai que mon controleur ne fait pas grand chôse par rapport au reste du systéme, mais le role du controleur n'est il pas juste de réagir aux actions de l'utilisateurs? |
Si, c'est uniquement ça...
Marsh Posté le 21-09-2005 à 11:26:45
skeye a écrit : Euh oui, et? |
La partie qui gére l'affichage, elle est pas sencé faire partie de la section "vue" du MVC?
Marsh Posté le 21-09-2005 à 11:28:58
nraynaud a écrit : En pratique dans le monde du web, tu peux faire un pire cas avec acquisition de la connection+démarrage de transaction à la réception de la requette, et relachement + commit au moment du renvoit de la réponse. |
Là il faudrait un FrontController, avec Pre + PostFilter et un PageController.
Lorsque le FrontController analyse la requête, il regarde si l'action demandée nécessite une connexion. Si vrai, une connexion est faite, et une transaction est démarée. Puis il instancie le PageController qui va bien, qui va s'exécuter et faire sa tambouille. Puis le FrontController fait le commit (si aucune erreur) + ferme la connexion, et envoi la réponse au client...
Marsh Posté le 21-09-2005 à 11:32:05
omega2 a écrit : La partie qui gére l'affichage, elle est pas sencé faire partie de la section "vue" du MVC? |
ben si, mais où est-ce que j'ai parlé de la partie qui gère l'affichage, moi?
J'ai dit que j'accédais directement à la base sans surcouche dans le modèle, c'est tout...
Marsh Posté le 20-09-2005 à 11:15:47
Dernière discussion page 36 sur MVC et le rôle de chacun des composants
-----------
Post initial
Model View Controller
Introduction
92.3% (estimation à la con) des applis PHP sont consitués de plusieurs scripts, dans lesquels sont mélangés accès aux données, logique métier (business ou domain logic) et présentation. On le voit surtout ici, où souvent les requêtes SQL et la génération d'HTML se côtoient. Or, ça va strictement à l'encontre de l'un des fondement de la programmation : la séparation des couches. Dans un monde idéal, on aurait trois couche : une qui s'occupe de l'accès aux données et de la logique métier (model), une qui s'occupe de la présentation (view), et une dernière qui coordonne le tout en gérant ce que l'utilisateur fabrique (controller).
Rôle et fonctionnement de chaque couche
Model :
Le model va donc contenir tout la business logic et l'accès aux données. Il doit être indépendant des autres couche, et il ne doit même pas savoir qu'il est utilisé dans un MVC.
Un exemple de model tout simple serait :
class UserModel {
function add_user($user_name, $user_age) {
mysql_query("INSERT INTO user VALUES($user_name, $user_age)" );
}
function get_user($user_id) {
return mysql_fetch_assoc(mysql_query("SELECT * FROM user WHERE user_id = $user_id" ));
}
function get_average_age() {
// Bordel qui calcul la moyenne d'âge et qui la retourne
}
function list_users() {
// Retourne un tableau avec toute la liste des users
}
}
Note : cet exemple est tout simple, buggé, faillé, dépendant de la source de données, mais illustre à peu-près le truc.
View :
La vue récupère les données à partir du model, et s'occupe de les afficher. Le controller va donc instancier la bonne view, lui filer le model, et la vue va s'occuper de récupérer ce qu'elle veux.
Du genre :
class UserListView {
var $user_model;
function UserListView($user_model) {
$this->user_model = $user_model;
}
function render() {
echo '<table summary="user list">'
$user_list = $this->user_model->list_users();
foreach($user_list as $user) {
echo '<tr><td>', $user['name'], '</td><td>', $user['age'], '</td></tr>';
}
echo '</table>';
}
}
Note : en pratique, on utilisera un système de template genre Smarty ou XSLT
Controller :
Lui va s'occuper de gérer ce que l'utilisateur demande, va instancier le model et la bonne vue, puis gérer tout ça.
Exemple :
class UserController {
function run() {
switch($_GET['action']) {
case 'add':
$model = new UserModel();
$model->add_user($_POST['name'], $_POST['age']);
$view = new UserAddedView($model); // Vue lorsqu'un utilisateur a été ajouté
$view->render();
break;
case 'list':
$model = new UserModel();
$view = new UserListView($model);
$view->render();
break;
}
}
}
Note : exemple faillé et buggé, mais illustrant le truc de façon simple
Implémentation
Ce sera un peu le but de ce topic. MVC est plus un concept qu'une Design Pattern précise, les implémentations peuvent être différentes. Les exemples que j'ai donné ne sont qu'une implémentation possible.
Pour la view, on peut utiliser un truc comme dans l'exemple, ou passer par une template view (Smarty), ou une transform view (XSLT). Pour le controller, soit un switch comme dans l'exemple, mais très vite lourdingue, soit par un command pattern, où une action et une vue sont associée dans un objet Command... Pour le model, soit une classe qui mappe un objet (ici notre User), soit tout une table, avec là-dessus des DAO ou des DataMappers pour l'accès à la source de données...
Bien ! En espérant de pas avoir fait trop d'erreurs, je vais lancer ce topic avec mon implémentation à moi en cours de design. Donc ce que j'ai commencé, c'est niveau accès aux données. J'ai designer 3 Dao réutilisables par les model : TableDao, OrderedTableDao et HierarchyTableDao. Le premier est utilisé pour une table tout simple, avec des méthodes d'ajout, de modification, de listage, etc. Le suivant est là pour les tables dont les enregistrements sont ordonnés (pensez à une liste de rubrique, donc on doit pouvoir modifier l'ordre d'affichage). Le dernier est là pour les données hiérarchisées (liste de rubriques hiérarchisée avec liste d'articles dans chaque rubrique).
Les trois classes sont abstraites, va falloir en fournir une implémentation pour chaque source de données (MySql, XML, etc.). L'objectif est évidemment de pouvoir changer de source de données à la volée, tout en prenant en compte les différences (des données hiérarchisées ne seront pas représentées de la même manière dans une base MySql que dans un fichier XML).
Voici mon diagramme UML :
Bon, reste à designer les model, les controllers et les view . Pour les view, j'ai opté pour des templates XSLT... Va falloir un espèce de "XmlBinder" qui prend un model et le transforme en XML compréhensible par l'XSLT. Pour le controller, ça j'ai pas encore trop réfléchit
Donc à votre tour aussi de poser vos questions, d'exposer vos point de vue, de présenter votre implémentation etc...
Lectures intéressantes :
Message édité par FlorentG le 06-03-2008 à 10:35:43