Voici une version mise à jour du motif Modèle Vue Contrôleur qui adhère plus strictement à la définition de ce motif.
La première version de ce motif peut être trouvée ici. Grâce à des commentaires constructifs, je l’ai modifié pour être plus proche de la définition généralement admise du motif MVC. Cet article met à jour le code tout en laissant l’ancien article pour donner un aperçu du concept.
En aparté, j’ai découvert Phrame aujourd’hui : un portage du projet Jakarta Struts à PHP qui fournit d’excellents outils pour réussir à séparer le Modèle et la Vue (couplage lâche).
Le nouveau diagramme UML :

- MVC révisé
Pour le reste, je laisserai le code parler par lui-même pour qu’on puisse passer à autre chose...
Ressources
Jason E.Sweat a fait un excellent travail pour expliquer MVC dans l’édition Mai 2003 de la revue php Sur Internet Archive
ProductController.php
<?php
/**
* Controle le flux de l'application
*/
class ProductController {
var $model;
var $view;
//! Un constructeur.
/**
* Construit un nouvel objet ProductController
* @param object $dao une instance de la classe DataAccess
*/
function ProductController (& $dao) {
$this->model=& new ProductModel($dao);
}
}
class ProductItemController extends ProductController {
//! Un constructeur.
/**
* Construit un nouvel objet ProductItemController
*
* @param object $dao une instance de la classe DataAccess
* @param array $getvars variables HTTP GET reçues
*/
function ProductItemController (& $dao,$getvars=null) {
ProductController::ProductController($dao);
$this->view=& new ProductItemView($this->model,$getvars['id']);
}
function & getView () {
return $this->view;
}
}
class ProductTableController extends ProductController {
//! Un constructeur.
/**
* Construit un nouvel objet ProductTableController
*
* @param object $dao une instance de la classe DataAccess
* @param array $getvars variables HTTP GET reçues
*/
function ProductTableController (& $dao,$getvars=null) {
ProductController::ProductController($dao);
if ( !isset ($getvars['rowsperpage']) )
$rowsperpage=20;
if ( !isset ($getvars['rownum']) )
$getvars['rownum']=0;
$this->view=& new ProductTableView($this->model,
$rowsperpage,
$getvars['rownum']);
}
function & getView () {
return $this->view;
}
}
?>
|
ProductView.php
<?php
/**
* Lie les données d'un Produit à la génération du HTML
*/
class ProductView {
/**
* Une instance de la classe ProductModel
*
* @access private
* @var object
*/
var $model;
/**
* Le code HTML généré est stocké ici pour affichage
*
* @access private
* @var string
*/
var $output;
//! Un constructeur.
/**
* Construit un nouvel objet ProductView
*
* @param object $model une instance de la classe ProductModel
*/
function ProductView (&$model) {
$this->model=& $model;
}
//! Un manipulateur
/**
* Crée le haut d'une page HTML
*
* @return void
*/
function header () {
$this->output=<<<EOD
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> Nos Produits </title>
<style>
body { font-size: 13.75px; font-family: verdana }
td { font-size: 13.75px; font-family: verdana }
.title { font-size: 15.75px; font-weight: bold; font-family: verdana }
.heading {
font-size: 13.75px; font-weight: bold;
font-family: verdana; background-color: #f7f8f9 }
.nav { background-color: #f7f8f9 }
</style>
</head>
<body>
<div align="center" class="title">Nos Produits</div>
EOD; $this->output.="\n<div align=\"right\"><a href=\"".
$_SERVER['PHP_SELF']."\">Début</a></div>\n";
}
//! Un manipulateur
/**
* Crée le bas d'une page HTML
*
* @return void
*/
function footer () {
$this->output.="</body>\n</html>";
}
}
class ProductItemView extends ProductView {
/**
* ID du produit à afficher
*
* @access private
* @var int
*/
var $productID;
//! Un constructeur
/**
* Construit un nouvel objet ProductView
*
* @param object $model instance de la classe ProductModel
*/
function ProductItemView (&$model,$productID) {
ProductView::ProductView($model);
$this->productID=$productID;
}
//! Un manipulateur
/**
* Affiche un produit unique
*
* @return void
*/
function productItem() {
$this->model->listProduct($this->productID);
while ( $product=$this->model->getProduct() ) {
$this->output.="<p><b>Nom</b>:".$product['PRODUCTNAME']."</p>".
"<p><b>Prix</b>:".$product['UNITPRICE']."</p>".
"<p><b># en stock</b>:".$product['UNITSINSTOCK']."</p>";
if ( $this->$product['DISCONTINUED']==1 ) {
$this->output.="<p>Ce produit n'est plus commercialisé.</p>";
}
}
}
//! An accesseur
/**
* Retourne le code HTML généré
*
* @return string
*/
function toString () {
$this->header();
$this->productItem();
$this->footer();
return $this->output;
}
}
class ProductTableView extends ProductView {
/**
* nombre de résultats par page
*
* @access private
* @var int
*/
var $rowsPerPage;
/**
* ID de l'enregistrement à partir duquel commencer l'affichage
*
* @access private
* @var string
*/
var $rowNum;
//! Un constructeur.
/**
* Construit un nouvel objet ProductView
*
* @param object $model instance de la classe ProductModel
*/
function ProductTableView (&$model,$rowsPerPage=20,$rowNum=0) {
ProductView::ProductView($model);
$this->rowsPerPage=$rowsPerPage;
$this->rowNum=$rowNum;
}
//! Un manipulateur
/**
* Crée une table (HTML) de produits
*
* @return void
*/
function productTable() {
$this->model->listProducts($this->rowNum,$this->rowsPerPage);
$this->output.="<table width=\"600\" align=\"center\">\n<tr>\n".
"<td class=\"heading\">Désignation</td>\n".
"<td class=\"heading\">Prix</td>\n</tr>\n";
while ( $product=$this->model->getProduct() ) {
$lastID=$product['PRODUCTID'];
$this->output.="<tr>\n<td><a href=\"".$_SERVER['PHP_SELF'].
"?view=product&id=".$product['PRODUCTID']."\">".
$product['PRODUCTNAME']."</a></td>".
"<td>".$product['UNITPRICE']."</td>\n</tr>\n";
}
$this->output.="<tr class=\"nav\">\n";
if ( $this->rowNum > 0 && $this->rowNum > $this->rowsPerPage ) {
$this->output.="<td><a href=\"".$_SERVER['PHP_SELF'].
"?view=table&rownum=".($this->rowNum-$this->rowsPerPage).
"\"><< Prev</a></td>";
} else {
$this->output.="<td> </td>";
}
if ( $this->rowsPerPage <= ( $lastID - $this->rowNum ) ) {
$this->output.="<td><a href=\"".$_SERVER['PHP_SELF'].
"?view=table&rownum=".($this->rowNum+$this->rowsPerPage).
"\">Next >></a></td>";
} else {
$this->output.="<td> </td>\n";
}
$this->output.="</tr>\n</table>\n";
}
//! Un accesseur
/**
* Retourne le code HTML généré
*
* @return string
*/
function toString () {
$this->header();
$this->productTable();
$this->footer();
return $this->output;
}
}
?>
|
et finalement le contrôleur principal revisité, index.php :
require_once('lib/DataAccess.php'); require_once('lib/ProductModel.php'); require_once('lib/ProductView.php'); require_once('lib/ProductController.php'); $dao=& new DataAccess ('localhost','root','password','mvc2'); switch ( $_GET['view'] ) { case "product": $controller=& new ProductItemController($dao,$_GET); break; default: $controller=& new ProductTableController($dao,$_GET); break; } $view=$controller->getView(); echo ("<pre>" ); // print_r($controller); echo ("</pre>" ); echo ($view->toString());
|