MVC est un paradigme de conception de systèmes interactifs. C’est une façon de les structurer en séparant trois concepts :
MODÈLE
, modél-isant la couche métier et en assurant la persistance,VUE
, qui sert d’interface avec l’utilisateur, le plus souvent en lecture,CONTRÔLEUR
, qui orchestre le tout et contient le plus gros de la
logique de l’application.Une telle séparation permet de :
M
, V
et C
(voir plus
loin).Au cours de cette activité, nous allons construire une application web de type CRUD, pour :
Voir l’article Wikipedia correspondant.
Nous utiliserons le langage PHP (tant pis) et organiserons notre code selon un autre paradigme important, la Programmation Orientée Objet (tant pis).
Les données seront stockées dans une base MySQL (tant pis).
Certains cadriciels PHP pour applications web sont conçus autour de MVC (voir une liste plus bas), mais ici, pour des raisons pédagogiques, nous n’utiliseront pas de framework moderne (tant pis). En revance, nous utiliserons un framework pédagogique : my.very.cute (voir plus bas).
Travailler avec un cadriciel PHP n’est pas destiné aux débutants. Cependant, une fois compris le statut de générateur de HTML, et avec quelques notions de programmation objet, l’utilisation d’un cadriciel est souhaitable :
Pour installer Apache, MySQL et PHP sur Windows, il existe de nombreux projets qui englobent le tout. UWAMP est un de ceux-là. Sinon :
Nous appliquerons autant que possible la méthode TDD, pour Tests Driven Development. Voir l’article Wikipedia.
Pour cela nous utiliserons urlrulz, un petit programme en Python3 qui teste des sites automatiquement.
Tant qu’on est dans les bonnes pratiques, un suivi des versions de votre travail peut être intéressant.
git
bien sûr !
Dans une application web, il gère généralement les traitements (parfois appelés la logique) à effectuer suivant différentes informations comme :
POST
ées,Certains traitements consisteront en une lecture ou une écriture dans la
couche de persistance, au travers donc du M
. Pour afficher les résultats,
il les transmettra à V
qui se chargera de la présentation.
M
ni le V
.index.php
, le répartiteur (dispatcher en anglais), aussi appelé
front controller,controller.php
, le contrôleur, aussi appelé back controller.mvc
de la racine.index.php
, bien configurer la constante BASE
, adresse à laquelle
le répertoire contenant index.php
sera accessible, par exemple :http://127.0.0.1/mvc/
.another method
qui est
codé dans le contrôleur,Voici un exemple de plan classique d’un site de type CRUD :
index.php
, l’URL est composée de segments délimités par des /
.GET
pour encoder l’URL :index.php
(besoin de configurer un rewrite sur le serveur) on en ajoutant
un faux .html
(en fait en le supprimant dans req_URL
).Télécharger (ou cloner) urlrulz et vérifier qu’il fonctionne bien.
Dans controller.php
, écrire les méthodes correspondant au plan du site
défini dans le tableau ci-dessus. Le site devra passer les tests
urlrulz
ci-dessous (téléchargeables ici) :
http://127.0.0.1/mvc/
http://127.0.0.1/mvc/index.php
http://127.0.0.1/mvc/index.php/
http://127.0.0.1/mvc/index.php/persos
http://127.0.0.1/mvc/index.php/persos/html
http://127.0.0.1/mvc/index.php/persos/csv
http://127.0.0.1/mvc/index.php/perso/3
http://127.0.0.1/mvc/index.php/perso/3/html
http://127.0.0.1/mvc/index.php/perso/3/csv
http://127.0.0.1/mvc/index.php/perso/5
http://127.0.0.1/mvc/index.php/perso/5/html
http://127.0.0.1/mvc/index.php/perso/5/csv
http://127.0.0.1/mvc/index.php/nouveau
http://127.0.0.1/mvc/index.php/edit/3
http://127.0.0.1/mvc/index.php/delete/3
La simple existence de méthodes bien choisies permettra d’éviter des 404 pour ces URLs.
Attention : Il se peut que votre serveur local ne soit pas configuré correctement pour cette activité. Après avoir fait exprès de faire une erreur de syntaxe PHP dans le contrôleur, vérifier qu’une erreur de type 500 est renvoyée au client.
Désactiver le mode débogage et, à l’aide d’instructions echo
bien senties,
débrouillez-vous pour que le site passe les tests urlrulz
ci-dessous
(téléchargeables ici) :
http://127.0.0.1/mvc/ accueil
http://127.0.0.1/mvc/index.php accueil
http://127.0.0.1/mvc/index.php/ accueil
http://127.0.0.1/mvc/index.php/persos liste html
http://127.0.0.1/mvc/index.php/persos/html liste html
http://127.0.0.1/mvc/index.php/persos/csv liste csv
http://127.0.0.1/mvc/index.php/perso/3 perso 3 html
http://127.0.0.1/mvc/index.php/perso/3/html perso 3 html
http://127.0.0.1/mvc/index.php/perso/3/csv perso 3 csv
http://127.0.0.1/mvc/index.php/perso/5 perso 5 html
http://127.0.0.1/mvc/index.php/perso/5/html perso 5 html
http://127.0.0.1/mvc/index.php/perso/5/csv perso 5 csv
http://127.0.0.1/mvc/index.php/nouveau nouveau
http://127.0.0.1/mvc/index.php/edit/3 modif perso 3
http://127.0.0.1/mvc/index.php/delete/3 suppression perso 3
Il faut que la réponse du site (ce que la méthode echo
ra) soit exactement ce
qui est proposé ci-dessus. Penser à utiliser les paramètres optionnels.
Une erreur de correspondance surprenante pourrait arriver :
...
Testing http://127.0.0.1/mvc/index.php/persos liste html...
At http://127.0.0.1/mvc/index.php/persos liste html, expected
'html' but got
<!-- du HTML, du HTML, et encore du HTML en pagaille, à savoir reconnaître -->
Le MODÈLE
modélise la couche métier et s’occupe de la persistance. En
d’autres termes, il réalise une abstraction sous deux angles :
Pour l’instant, nous n’incorporons pas le V
.
Créer une base de données MySQL nommée personnages
, puis initialisez-là avec
ce script.
Voir TP précédent, dont une correction est fournie plus loin.
Une classe s’occupe de modéliser un personnage, une autre s’occupe de gérer ces personnages.
Il est possible d’utiliser des tests unitaires contre le modèle. Voir ce squelette.
index.php
:
// Include model files.
foreach (glob("m_*.php") as $filename) include $filename;
public function la_suite() {
$mng = new PersonnagesManager();
foreach ($mng->getList() as $perso) {
echo $perso->getNom();
}
}
À quelle URL peut-on tester ces dernières lignes de PHP ?
Objectif : ces tests urlrulz
(téléchargeables
ici) doivent passer.
http://127.0.0.1/mvc/ accueil
http://127.0.0.1/mvc/index.php accueil
http://127.0.0.1/mvc/index.php/ accueil
http://127.0.0.1/mvc/index.php/perso/3 <b>Fou</b>
http://127.0.0.1/mvc/index.php/perso/3/html <b>Fou</b>
http://127.0.0.1/mvc/index.php/perso/3/csv Fou
http://127.0.0.1/mvc/index.php/persos Roi Reine Fou Tour Combattant Soldat Garde Fantassin Alerte Déesse Amazone
http://127.0.0.1/mvc/index.php/persos/html Roi Reine Fou Tour Combattant Soldat Garde Fantassin Alerte Déesse Amazone
http://127.0.0.1/mvc/index.php/persos/csv Roi,Reine,Fou,Tour,Combattant,Soldat,Garde,Fantassin,Alerte,Déesse,Amazone
Compléter le projet pour en faire un site navigable, toujours à l’aide de
l’instruction echo
, et toujours sans aucun formulaire : que des lectures.
Attention : Dans un premier temps, on ne cherche pas à produire du
HTML de compétition. Ne tapez que le strict minimum au niveau des balises (pas
de <html>
, <head>
, <title>
…, mais des <h1>
, <a>
, <ul>
…) et ne
cherchez pas à faire beau.
Juste navigable, avec
par exemple :
Note : Cette partie est fastidieuse, vous pourrez y revenir une fois que le concept de vue aura été compris.
Compléter le projet pour que l’on puisse opérer des créations, modifications et des suppressions. Vous aurez besoin de gérer des formulaires, à l’ancienne.
C’est ce que le V
va tenter de résoudre…
C’est le conteneur qui parvient au client. Il contient et présente à l’utilisateur des informations, de manière organisée.
Dans le cas du web :
M
de MVC !!!).Liste non exhaustive :
On a parfois besoin de traitements dans les vues :
C’est comme ça.
On utilise la procédure view
avec deux arguments seulement. Le HTML est
directement envoyé au navigateur.
index.php
:
public function persos($display_type = 'html') {
$mng = new PersonnagesManager();
if ($display_type == 'html') {
header("Content-Type:text/html");
view('v_liste_persos.php', array('persos' => $mng->getList()));
} else if ($display_type == 'csv') {
header("Content-Type:text/plain");
echo view('v_liste_persos_csv.php', array('persos' => $mng->getList()), true);
} else {
echo "Type inconnu.";
}
}
Dans my.very.cute
, seules ces lignes de la procédure view
nous
intéressent pour l’instant :
function view($view_file, $vars = array(), ...) {
...
if (file_exists($view_file)) include($view_file); // (1)
else echo '<pre>'.print_r($vars, true).'</pre>'; // (2)
...
$view_file
existe, on l’exécute. $vars
contenant
les données que le contrôleur passe à cette vue afin qu’elle les affiche.$vars
(sorte d’outil de debug
rustique).Notez que si votre version de PHP le permet, il est possible d’utiliser <?=
à
la place de <? echo
. C’est plus joli dans les vues.
On va utiliser le système de capture de sortie de PHP, pour pouvoir affecter en
cette sortie (chaîne de caractère) à une variable et l’inclure dans une autre
vue. On passe cette variable à la vue comme une vulgaire donnée, via le
tableau $vars
(deuxième paramètre de view
).
$sous_vue = view('v_ma_vue.php', $vars, true)
view('v_page_globale.php', array('sous_vue' => $sous_vue))
Ou plus directement :
view('v_page_globale.php', array('sous_vue' => view('v_ma_vue.php', $vars, true))
Après avoir créé une v_page_globale.php
, contenant du HTML valide (à base de
<html>
, <head>
, <title>
…), faîtes en sorte que toutes vos pages aient
un logo et un pied de page.
Il est possible d’utiliser des moteurs de vue plus évolués que celui défini
dans la procédure view
. Certains frameworks utilisent leur propre système,
ou utilisent des projets dédiés au vues :
Après en avoir choisi un, intégrez-le à ce projet.
M
et V
, on dit que l’on a plutôt
une structure Model-View-Presenter.Plusieurs types d’arborescence sont possibles :
mvc
├── controlers
│ ├── add.php
│ └── list.php
├── models
│ ├── article.php
│ └── comment.php
└── views
├── article.php
├── footer.php
├── header.php
└── menu.php
mvc
├── main
│ ├── controlers
│ │ ├── add.php
│ │ └── list.php
│ ├── models
│ │ ├── article.php
│ │ └── comment.php
│ └── views
│ ├── article.php
│ ├── footer.php
│ ├── header.php
│ └── menu.php
└── wiki
├── controlers
│ ├── add_page.php
│ └── list_pages.php
├── models
│ ├── comment.php
│ └── page.php
└── views
├── footer.php
├── header.php
├── menu.php
└── page.php
Avec tout de même une convention pour s’y retrouver, comme dans l’activité où
les modèles commencent par m_
et les vues par v_
.
Pour les comparer : http://phpframeworks.com.
En bonus, quelques routeurs/dispatcheurs/répartiteurs :