Model View Controller (MVC) - Architecture des applications PHP

Model View Controller (MVC) - Architecture des applications PHP - PHP - Programmation

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 :
 
http://img400.imageshack.us/img400/2780/dao6ax.th.png
 
Bon, reste à designer les model, les controllers et les view [:dawa]. 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 cité 1 fois
Message édité par FlorentG le 06-03-2008 à 10:35:43
Reply

Marsh Posté le 20-09-2005 à 11:15:47   

Reply

Marsh Posté le 20-09-2005 à 11:20:18    

[:drapo]
 
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...[:dawa]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 20-09-2005 à 11:21:07    

Comment ça, "le controleur est le script" ?

Reply

Marsh 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)...[:joce]
C'est le script qui joue ce rôle, donc...[:dawa]


Message édité par skeye le 20-09-2005 à 11:22:31

---------------
Can't buy what I want because it's free -
Reply

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...

Reply

Marsh Posté le 20-09-2005 à 11:29:09    

UserModel, UserListView
 
miserable failure


---------------
trainoo.com, c'est fini
Reply

Marsh Posté le 20-09-2005 à 11:32:35    

C'est un exemple bidon et vite-fait ;)

Reply

Marsh Posté le 20-09-2005 à 11:32:39    

tu pourrais donner le lien de OnLamp aussi :O
 malgré ton tuto :jap:


---------------
IVG en france
Reply

Marsh Posté le 20-09-2005 à 11:33:00    

Ouais, je vais rajouter 2-3 liens

Reply

Marsh Posté le 20-09-2005 à 11:36:06    

nraynaud a écrit :

UserModel, UserListView
 
miserable failure


Hésite pas aussi à faire tes remarques, en prenant soin de compléter, de corriger, d'argumenter, d'expliquer [:dawa]

Reply

Marsh Posté le 20-09-2005 à 11:36:06   

Reply

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.


---------------
trainoo.com, c'est fini
Reply

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 :jap: 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 [:dawa]).
 

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 :jap:

Reply

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.

Reply

Marsh Posté le 20-09-2005 à 12:08:15    

[:drapo] aussi. J'pensais faire qqchose comme ça pour un futur site, c'est donc bienvenu.


---------------
StarCraft Professional Gaming Database | [Ze Topic] Starcraft/BroodWar
Reply

Marsh Posté le 21-09-2005 à 07:31:44    

Je comprends pas tout mais ça à l'air sympa ! [:dawa]

Reply

Marsh Posté le 21-09-2005 à 08:00:14    

stiffler a écrit :

Je comprends pas tout mais ça à l'air sympa ! [:dawa]


il est beau le bac+5 [:ula]

Reply

Marsh Posté le 21-09-2005 à 08:02:47    

uriel a écrit :

il est beau le bac+5 [:ula]


Sur le bac +5 j'en ai 4 en chmie-biologie !  [:jocefuck]  [:jocefuck]


Message édité par stiffler le 21-09-2005 à 08:02:55
Reply

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  :jap:


---------------
http://www.alsacreations.com , http://www.openweb.eu.org. Mon CV : http://cv.roane-irkana.net/. Exemple à ne surtout pas suivre : www.worldinternet.be
Reply

Marsh Posté le 21-09-2005 à 09:12:54    

Moi j'ai dû tout apprendre tout seul :sweat:

Reply

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.


Message édité par uriel le 21-09-2005 à 09:18:56

---------------
IVG en france
Reply

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)

Reply

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...[:pingouino]


---------------
Can't buy what I want because it's free -
Reply

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 [:dawa]
 
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 ?

Reply

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...[:pingouino]


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

Reply

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...:o


---------------
Can't buy what I want because it's free -
Reply

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...[:petrus75]
Une autre solution serait de le référencer dans tous mes objets, mais ça me plait pas des masses non plus... :/


---------------
Can't buy what I want because it's free -
Reply

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.

Reply

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.
 
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.


 
ah oui, pas bête.[:petrus75]


---------------
Can't buy what I want because it's free -
Reply

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...[:pingouino]


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.


---------------
trainoo.com, c'est fini
Reply

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. :ange:  
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. :lol:

Reply

Marsh Posté le 21-09-2005 à 11:17:11    

nraynaud a écrit :

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.


 
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...:o


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 21-09-2005 à 11:18:24    

omega2 a écrit :

Alors c'est plus du MVC. :ange:  
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.


 
Euh oui, et?
J'accède à la base dans le modèle, pas dans la vue ni dans le pseudo-contrôleur.:o


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 21-09-2005 à 11:20:11    

nraynaud a écrit :

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.


Voilà :jap: 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.  

  • Un générique pour tout ce qui vient de l'internaute. Genre il prend une URL du type : http://domain.tld/news/list.html, avec laquelle il appellera la vue "List" et le modèle "News"... Assez basique dans l'implémentation
  • Un pour tous les formulaires, donc simplement gérer tout ce qui est ajout, modification, suppression dans l'interface d'admin, gérer les formulaires multi-pages, etc...


Ca sert plus de chef d'orchestre qu'autre chose

Reply

Marsh Posté le 21-09-2005 à 11:21:11    

omega2 a écrit :

Alors c'est plus du MVC. :ange:


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...

Reply

Marsh Posté le 21-09-2005 à 11:24:16    

nraynaud a écrit :

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.


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?

Reply

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?
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...[:petrus75]
Une autre solution serait de le référencer dans tous mes objets, mais ça me plait pas des masses non plus... :/


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.


---------------
trainoo.com, c'est fini
Reply

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...

Reply

Marsh Posté le 21-09-2005 à 11:26:45    

skeye a écrit :

Euh oui, et?
J'accède à la base dans le modèle, pas dans la vue ni dans le pseudo-contrôleur.:o


La partie qui gére l'affichage, elle est pas sencé faire partie de la section "vue" du MVC?

Reply

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...

Reply

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? [:pingouino]
J'ai dit que j'accédais directement à la base sans surcouche dans le modèle, c'est tout...[:urd]


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed