Architecture de type MVC avec PHP¶
Problème¶
Lorsqu’un projet augmente, le besoin de s’organiser et de permettre plus de réutilisabilité et de lisibilité demande une certaine méthode. MVC = Modèle Vue Controleur peut être une solution intéressante.
Nous allons commencer à nous familiariser avec les composants d’un Framework MVC et à voir l’utilité de recourir à de tels outils.
Une introduction générale à ce sujet se trouve ici
Du PHP pur aux templates PHP:¶
Considérons le code suivant en interrogeant la table CARNET vue précemment depuis PHP avec PDO:
<?php
require("connect.php");
$dsn="mysql:dbname=".BASE.";host=".SERVER;
try{
$connexion=new PDO($dsn,USER,PASSWD);
}
catch(PDOException $e){
printf("Echec connexion : %s\n", $e->getMessage());
exit();
}
$sql="SELECT * from CARNET";
if(!$connexion->query($sql))
echo "Pb pour acceder au CARNET";
else
{
foreach ($connexion->query($sql) as $row){
echo $row['NOM']<br/>\n";
}
}
?>
On peut observer quelques défauts dans le code ci-dessus:
Réutilisabilté du code très réduite
Si on fabrique un formulaire avec les entrées du carnet, où doit-on mettre le code correspondant ?
Un template PHP:¶
On peut améliorer un peu les choses:
<?php
require("connect.php");
$dsn="mysql:dbname=".BASE.";host=".SERVER;
try
{
$connexion=new PDO($dsn,USER,PASSWD);
}
catch(PDOException $e)
{
printf("Echec connexion : %s\n", $e->getMessage());
exit();
}
$sql="SELECT * from CARNET";
if(!$connexion->query($sql))
echo "Pb pour acceder au CARNET";
else
{
$amis=Array();
foreach ($connexion->query($sql) as $row){
$amis[]=$row;
}
require "templates/listeamis.php";
}
?>
Avec un template listeamis.php à placer dans templates/listeamis.php:
<!DOCTYPE html>
<html>
<head>
<title>Liste de mes Amis</title>
</head>
<body>
<h1>List of friends</h1>
<ul>
<?php foreach ($amis as $ami): ?>
<li>
<a href="/recherche?nom=<?php echo $ami['ID'] ?>">
</a>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>
On commence ainsi à séparer la présentation du codage « métier ».
Isolons la logique applicative:¶
<?php
//modele.php
require("connect.php");
function connect_db()
{
$dsn="mysql:dbname=".BASE.";host=".SERVER;
try
{
$connexion=new PDO($dsn,USER,PASSWD);
}
catch(PDOException $e)
{
printf("Echec connexion : %s\n",
$e->getMessage());
exit();
}
return $connexion;
}
// Puis
function get_all_friends()
{
$connexion=connect_db();
$amis=Array();
$sql="SELECT * from CARNET";
foreach ($connexion->query($sql) as $row)
{
$amis[]=$row;
}
return $amis;
}
?>
On peut maintenant avoir un controleur très simple qui interroge le modèle puis passe les données au template pour affichage.
<?php
//c-list.php
require_once 'modele.php';
$amis = get_all_friends();
require 'templates/listamis.php';
?>
Layout:¶
Il reste une partie non réutilisable dans le code à savoir le layout. Essayons de remédier à ça:
<!-- templates/baseLayout.php -->
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title ?></title>
</head>
<body>
<?php echo $content ?>
</body>
</html>
Héritage de templates:¶
<?php
// templates/t-list.php
$title = 'Liste des amis';
ob_start();
?>
<h1>List de mes amis</h1>
<ul>
<?php foreach ($amis as $ami): ?>
<li>
<a href="/recherche?nom=<?php echo $ami['nom'] ?>">
<?php echo $ami['VILLE'] ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php
$content = ob_get_clean();
include 'baseLayout.php'
?>
Observez l’utilisation de la bufferisation avec ob_start() et ob_get_clean(). Cette dernière fonction récupère le contenu bufferisé et nettoie ensuite le buffer.
Affichage des détails d’une personne¶
On va ajouter à notre modèle une fonction pour afficher les détails d’une personne:
<?php
function get_friend_by_id($id)
{
$connexion=connect_bd();
$sql="SELECT * from CARNET where ID=:id";
$stmt=$connexion->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetch();
}
On peut maintenant créer un nouveau controleur c-details.php :
<?php
//c-details.php
require_once 'modele.php';
$pers = get_friend_by_id($_GET['id']);
require 'templates/t-details.php';
?>
Qui utilise le template:
<?php
//templates/t-details.php
$title = $pers['NOM'];
ob_start();
?>
<h1>details sur
<?php echo $pers['PRENOM'].' '.$pers['NOM'] ?>
</h1>
<p>
<?php
echo ' Ne le '.$pers['NAISSANCE'];
echo '<br/>Ville:'.$pers['VILLE'];
$content = ob_get_clean();
include 'baseLayout.php'
?>
Vous pouvez tester en entrant l’URL de c-details.php avec un paramètre id. Le code est similaire à celui du premier template et nous pouvons réutiliser le template de base, mais il subsiste plusieurs problèmes:
Si le paramètre id n’est pas fourni, notre application va provoquer une erreur.
Nous n’avons pas de controleur principal.
Regroupons d’abord le code des 2 contrôleurs (c-list.php et c-details.php) dans un fichier unique controllers.php
<?php
// controllers.php
function list_action()
{
$amis = get_all_friends();
require 'templates/t-list.php';
}
function detail_action($id)
{
$pers = get_friend_by_id($id);
require 'templates/t-detail.php';
}
?>
Nous pouvons enfin proposer un controleur principal (Front Controller) index.php
:
<?php
// index.php
// On charge les modeles et les controleurs
require_once 'modele.php';
require_once 'controllers.php';
// gestion des routes
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ('/index.php' == $uri)
{
list_action();
}
elseif ('/index.php/detail' == $uri && isset($_GET['id']))
{
detail_action($_GET['id']);
}
else
{
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
?>
Nous avons maintenant une structure de ce type:
├── connect.php
├── connexion.php
├── controlleur.php
├── modele.php
├── recherche.php
└── templates
├── layout.php
└── listeamis.php
On peut améliorer tout cela en intégrant dans un même Objet tout le modèle:
Les templates PHP présentés ne sont pas très simples à utiliser, aussi nous allons étudier un système de templating plus puissant: Twig.