Programmation PHP/Exemples/MVC

Un livre de Wikilivres.

Historiquement, PHP est un langage glue, il peut être intégré avec le langage de balisage HTML. L'avantage est cette simplicité de mise-en-œuvre mais l'inconvénient est le mélange entre le traitement et l'affichage. Pour produire une application web claire et facile à entretenir, on peut séparer les différentes parties de l'application selon l'architecture Modèle-Vue-Contrôleur (ou MVC).

  1. Modélisation (Modèle : Partie métier spécifique à l'application)
  2. Visualisation (Vue : Partie visuelle de l'application)
  3. Contrôles (Contrôleur : Partie de gestion des événements de l'application)

De cette façon on peut implémenter son application en sous composantes ce qui augmente légèrement l'analyse de l'application mais fera gagner beaucoup de temps de développement par la suite. Cette architecture est déjà couramment employée dans les applications locales et les applications web l'utilise de plus en plus. On remarquera notamment que beaucoup de Cadriciel web sont basés sur ce principe.

Développement[modifier | modifier le wikicode]

  • Objectif : Faire un mini système de validation de données saisies.

Pour ce faire on a besoin :

  1. d'un formulaire HTML
  2. d'un module de validation
  3. d'un module de gestion homme-machine
  • Pré-requis conseillés :
  1. html4
  2. php5

Création de la vue[modifier | modifier le wikicode]

La vue comprendra un formulaire HTML pour la saisie des données utilisateur :

<html>
	<head>
	</head>
	<body>
		<form id="fForm" name="fForm" action="" method="POST">
			<!-- données utilisateur -->
			<input type="text" id="iNom" name="iNom" /><br/>
			<input type="text" id="iPrenom" name="iPrenom" /><br/>
			<input type="text" id="iVisa" name="iVisa" /><p/>
			<input type="submit" value="envoyer" /> 
		</form>
	</body>
</html>

Spécialisation des composantes[modifier | modifier le wikicode]

Il est mieux de découper son application en sous composante. La vue par exemple pourrait contenir :

  1. le frameset de la page
  2. les containers à afficher

1. Le frameset (vueFrame.inc.php) :

<?php

$page['container']['frameSet'] = '
  <html>
    <head>'
      .$page['css']
      .$page['script'].
    '</head>
    <body>'
      .$page['container']['header']
      .$page['container']['main']
      .$page['container']['footer'].
    '</body>
  </html>';

?>

2. Les formulaires deviendraient (vueFormulaire.inc.php) :

<?php

$page['container']['visa']='
  <div id="cVisa">
    <form id="fForm" name="fForm" action="" method="POST">
      <!-- données utilisateur -->
      <input type="text" id="iNom" name="iNom" value="'.$value['user']['nom'].'"/>
      <span id="msgNom">'
        // communication nom
        .$msg['error']['nom'].'</span><br/>
      <input type="text" id="iPrenom" name="iPrenom" value="'.$value['user']['prenom'].'"/>
      <span id="msgPrenom">'
        // communication prenom
        .$msg['error']['prenom'].'</span><br/>
      <input type="text" id="iVisa" name="iVisa" value="'.$value['user']['visa'].'"/>
      <span id="msgVisa">'
        // communication visa
        .$msg['error']['visa'].'</span><p/>
      <input type="submit" value="envoyer" /> 
    </form>
  </div>';

$page['container']['form1']='
  <div id="">
    <!-- autre formulaire -->
    <form>
    </form>
  </div>';

?>

Création du modèle[modifier | modifier le wikicode]

Cette bibliothèque utilitaire reprendra les traitements de validation

On a besoin des fonctions suivantes :

  1. check des string alphabétiques
  2. check d'un numéro VISA (4 groupes de 4 chiffres avec le premier groupe commençant par 4 pour simplifier)
<?php

/* modeleValidation

   SYNOPSIS : Cette bibliothèque reprend toutes les fonctions de validation de l'application
*/

/* isAlpha : contrôle de chaîne alphabétique
   @author	: nom
   @date	: 04.11.2007
   @version     : 1
*/
function isAlpha($str)
{// 	>((string)str)-(bool)>

return preg_match('/^([a-zA-Z]*)$/',$str);
}//

/* isVisa : contrôle de numéro VISA
   @author 	: nom
   @date	: 04.11.2007
   @version     : 2
*/
function isVisa($str)
{//	>((string)str)-(bool)>

return preg_match('/^(4)([0-9]{15})$/',$str);
}//

/* DEPRECIEE - isVisa : contrôle de numéro VISA
   @author 	: nom
   @date	: 07.05.2006
   @version     : 1
function isVisa($str)
{//	>((string)str)-(bool)>

return preg_match('/^([0-9]{16})$/',$str);
}//
*/

?>

! Ce modèle modeleValidation.inc.php par exemple comporte une nouvelle fonction isVisa remplaçant celle du 07.05.2006.

Création du contrôleur[modifier | modifier le wikicode]

Le contrôleur regroupe les traitements de gestion de l'application, c'est à dire ici les événements déclenchés par l'utilisateur ou par le système. On parle souvent d'IHM, voilà un exemple.

On a besoin des contrôles suivants :

  1. validation de saisie
  2. affichage en fonction du statut de la saisie
<?php

/* controlAffichage

   SYNOPSIS : Cette page reprend tous les contrôles pour gérer l'affichage
*/

# INIT
require_once "./Modele/modeleValidation.inc.php";

// var de contrôle de données saisies
$checkSum=3;

# CONTROL
if($_POST)
{
  if(!isAlpha($_POST['iNom']))
  {
    $value['user']['nom'] = $_POST['iNom'];
    $msg['error']['nom']='nom invalide !';
    @$checkSum--;
  }
  if(!isAlpha($_POST['iPrenom']))
  {
    $value['user']['prenom'] = $_POST['iPrenom'];
    $msg['error']['prenom']='prenom invalide !';
    @$checkSum--;
  }
  if(!isVisa($_POST['iVisa']))
  {
    $value['user']['visa'] = $_POST['iVisa'];
    $msg['error']['visa']='visa invalide !';
    @$checkSum--;
  }
}

# CHOIX DE L'OUTPUT
//if(!$checkSum)
if($checkSum==3)
{// formulaire validé
  //$page['container']['main'] = 'Formulaire valide';
  $msg['error']['alert'] = 'Formulaire valide';
}
else
{// formulaire invalide
  //$page['container']['main'] = $page['container']['visa'].'<p> Formulaire invalide !</p>';*/
  $msg['error']['alert'] = $page['container']['visa']
    .'<p> Formulaire invalide !</p>';
}

?>

! utilisation de $page['container']['main'] ne sert a rien ici car sera écrasé dans l'appel de la page principale.

Gestion des containers[modifier | modifier le wikicode]

Chaque container ou div est ici considéré comme un flux à gérer. Ce qui permettra par la suite d'évoluer simplement vers l'ajax.

Pour bien construire sa page sans mauvaise surprise, il vaut mieux :

  1. charger les containers du plus petit enfant au plus grand parent
  2. ne faire l'output que du frameset

Par ex. :

<?php

// page principale

# INIT
  @session_start();
  

# PREPARE PAGE

  $page['container']['header']='ceci est le header<p/>';
  $page['container']['footer']='ceci est le footer<p/>';

  require_once "./Control/controlAffichage.inc.php";

  require_once './Vue/vueFormulaire.inc.php';
  // preparation du container principal
  $page['container']['main']='
    <div>Veuillez introduire vos données utilisateur :</div>'
    .$page['container']['visa'];
  require_once './Vue/vueFrame.inc.php';
  
# OUTPUT final

  echo $page['container']['frameSet'];

?>

! d'abord $page['container']['main'] et en dernier $page['container']['frameSet']

! $page['container']['main'] de Control/controlAffichage.inc.php sera jamais affiché car il se fait écraser systématiquement avant ./Vue/vueFrame.inc.php' qui passe à l'affichage à proprement parler. Que faire?

Ajout de nouvelles fonctionnalités[modifier | modifier le wikicode]

On souhaiterait pouvoir gérer les communications dans plusieurs langues. Pour ce faire :

  1. on rajoute un modele messageList.inc.php
  2. on modifie un peu le contrôleur
  3. on crée ou modifie les composantes de la vue

1. messageList.inc.php

<?php

/* messageList

   SYNOPSIS : Cette liste reprend toutes les communications usuelles

   NOTA : Il est préférable d'en faire une table dans une DB
          pour décharger la RAM du serveur et de créer la fonction d'appel du message
*/

// par convention 1=fr et 2=uk

# ERRORS
$msg['error']['alpha'][1] = 'champ alpha invalide';
$msg['error']['alpha'][2] = 'invalid alpha field';
$msg['error']['num'][1] = 'champ numérique invalide';
$msg['error']['num'][2] = 'invalid numeric field';

$msg['error']['invalid'][1] = 'formulaire invalide !';
$msg['error']['invalid'][2] = 'invalid form !';

# SUCCESS
$msg['success']['valid'][1] = 'le formulaire a été validé';
$msg['success']['valid'][2] = 'the form has been validated';

# MESSAGE
$msg['communication']['form'][1] = 'veuillez introduire vos données utilisateur';
$msg['communication']['form'][2] = 'please introduce your user data';
?>

2. controlAffichage.inc.php devient

<?php

/* controlAffichage

   SYNOPSIS : Cette page reprend tous les contrôles pour gérer l'affichage
*/

# INIT
require_once "./Modele/modeleValidation.inc.php";
require_once "./Modele/messageList.inc.php";

// var de controle de données saisies
$_SESSION['checkSum']=3;

# CONTROL
if($_POST)
{
  if(!isAlpha($_POST['iNom']))
  {
    $value['user']['nom'] = $_POST['iNom'];
    $msg['error']['nom']= $msg['error']['alpha'][$lang];           // <--
    @$_SESSION['checkSum']--;
  }
  if(!isAlpha($_POST['iPrenom']))
  {
    $value['user']['prenom'] = $_POST['iPrenom'];
    $msg['error']['prenom']= $msg['error']['alpha'][$lang];        // <--
    @$_SESSION['checkSum']--;
  }
  if(!isVisa($_POST['iVisa']))
  {
    $value['user']['visa'] = $_POST['iVisa'];
    $msg['error']['visa']= $msg['error']['num'][$lang];           // <--
    @$_SESSION['checkSum']--;
  }
}

if($_SESSION['checkSum']==3)
{// formulaire validé
  $page['container']['main'] = $msg['success']['valid'][$lang];    // <--
}
else
{// formulaire invalide
  $page['container']['main'] .= $page['container']['visa']         // <--
    .'<p>'.$msg['error']['invalid'][$lang].'</p>';                 // <--
}

?>

3. ici rien est à faire

Application[modifier | modifier le wikicode]

L'application se construit ensuite par inclusion des composantes

<?php
 
// Main page
 
# INIT
  @session_start();

  // preparation de la langue choisie (fr par defaut)
  $lang = $_GET['lang']?$_GET['lang']:1;

 
# PREPARE PAGE
 
  $page['container']['header']='ceci est le header<p/>';
  $page['container']['footer']='ceci est le footer<p/>';
 
  // appel de controlAffichage
  // page['container']['main'] va etre remplace si le formulaire est valide
  require_once "./Control/controlAffichage.inc.php";

  require_once './Vue/vueFormulaire.inc.php';
  #region[1] mainPage
  // preparation du container principal
  $page['container']['main']='
    <div><a href="'.$_SERVER['PHP_SELF'].'?lang=1">fr</a> | <a href="'.$_SERVER['PHP_SELF'].'?lang=2">uk</a></div><p/>
    <div>'.$msg['communication']['form'][$lang].'</div>'
    .$page['container']['visa'];
  require_once './Vue/vueFrame.inc.php';
 
  #endRegion[1]

# OUTPUT final
 
  echo $page['container']['frameSet'];
 
?>

! Il y a encore moyen d'optimiser la region[1] pour ne pas avoir à initialiser le container main avant l'inclusion et de faire de controlAffichage.inc.php un contrôleur ne comprenant aucun autre traitement que l'appel pour affichage.

En bref[modifier | modifier le wikicode]

Cette façon de programmer permet de construire des applications plus souples et évolutives et ne demande pas plus de méthode que celle de choisir correctement ses variables lors de l'analyse. L'utilisation de classes permet d'optimiser plus encore le développement et faciliter l'ergonomie.

Il existe de nombreuses implémentations de MVC dans des cadriciels web écrit en PHP.