Aujourd'hui, nous découvrons le pattern MVC (Modèle-Vue-Contrôleur), une architecture logicielle qui sépare les responsabilités de notre application en trois composants distincts.
1. Qu'est-ce que le pattern MVC ?
Modèle-Vue-Contrôleur
Modèle (Model)
Rôle : Gère les données et la logique métier
- Accès à la base de données
- Validation des données
- Logique métier
Vue (View)
Rôle : Affiche les données
- HTML, CSS
- Présentation visuelle
- Interface utilisateur
Contrôleur (Controller)
Rôle : Fait le lien entre Modèle et Vue
- Reçoit les requêtes
- Appelle le Modèle
- Passe les données à la Vue
Flux d'une requête MVC
1. L'utilisateur visite : index.php?action=show&id=1
2. Le Contrôleur reçoit la requête
→ Quelle action ? show
→ Quel ID ? 1
3. Le Contrôleur appelle le Modèle
→ "Donne-moi l'article avec l'ID 1"
4. Le Modèle interroge la base de données
→ SELECT * FROM articles WHERE id = 1
5. Le Modèle retourne les données au Contrôleur
→ ["id" => 1, "titre" => "Mon article", "contenu" => "..."]
6. Le Contrôleur passe les données à la Vue
→ "Affiche cet article"
7. La Vue génère le HTML
→ <h1>Mon article</h1><p>...</p>
8. L'utilisateur voit la page HTML
Avantages du MVC :
- Séparation des responsabilités : Chaque partie a un rôle clair
- Maintenabilité : Facile de modifier une partie sans casser les autres
- Réutilisabilité : Un même Modèle peut servir plusieurs Vues
- Travail en équipe : Design (Vue), Back-end (Modèle), Logique (Contrôleur)
2. Structure des dossiers MVC
Organisation recommandée
projet-mvc/
│
├── public/
│ └── index.php
│
├── src/
│ ├── Controllers/
│ │ ├── HomeController.php
│ │ └── ArticleController.php
│ │
│ ├── Models/
│ │ ├── Article.php
│ │ └── User.php
│ │
│ └── Views/
│ ├── home/
│ │ └── index.php
│ ├── articles/
│ │ ├── list.php
│ │ └── show.php
│ └── layout/
│ ├── header.php
│ └── footer.php
│
├── config/
│ └── database.php
│
└── autoload.php
3. Le Modèle (Model)
Exemple : Modèle Article
<?php
namespace Models;
class Article {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
public function getAll() {
$sql = "SELECT * FROM articles ORDER BY date_creation DESC";
$stmt = $this->pdo->query($sql);
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
public function getById($id) {
$sql = "SELECT * FROM articles WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
$stmt->execute(['id' => $id]);
return $stmt->fetch(\PDO::FETCH_ASSOC);
}
public function create($titre, $contenu, $auteur) {
$sql = "INSERT INTO articles (titre, contenu, auteur, date_creation)
VALUES (:titre, :contenu, :auteur, NOW())";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
'titre' => $titre,
'contenu' => $contenu,
'auteur' => $auteur
]);
return $this->pdo->lastInsertId();
}
public function update($id, $titre, $contenu) {
$sql = "UPDATE articles SET titre = :titre, contenu = :contenu WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([
'id' => $id,
'titre' => $titre,
'contenu' => $contenu
]);
}
public function delete($id) {
$sql = "DELETE FROM articles WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute(['id' => $id]);
}
}
?>
4. Le Contrôleur (Controller)
Exemple : Contrôleur Article
<?php
namespace Controllers;
use Models\Article;
class ArticleController {
private $articleModel;
public function __construct($pdo) {
$this->articleModel = new Article($pdo);
}
public function index() {
$articles = $this->articleModel->getAll();
require __DIR__ . '/../Views/articles/list.php';
}
public function show($id) {
$article = $this->articleModel->getById($id);
if (!$article) {
die("Article non trouvé");
}
require __DIR__ . '/../Views/articles/show.php';
}
public function create() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$titre = trim($_POST['titre'] ?? '');
$contenu = trim($_POST['contenu'] ?? '');
$auteur = trim($_POST['auteur'] ?? '');
if (!empty($titre) && !empty($contenu) && !empty($auteur)) {
$id = $this->articleModel->create($titre, $contenu, $auteur);
header("Location: index.php?action=show&id=$id");
exit();
}
}
require __DIR__ . '/../Views/articles/create.php';
}
public function delete($id) {
$this->articleModel->delete($id);
header("Location: index.php?action=index");
exit();
}
}
?>
5. La Vue (View)
Exemple : Vue Liste des articles
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title>Liste des articles</title>
</head>
<body>
<h1>Liste des articles</h1>
<a href="index.php?action=create">Créer un article</a>
<?php foreach ($articles as $article): ?>
<div>
<h2>
<a href="index.php?action=show&id=<?php echo $article['id']; ?>">
<?php echo htmlspecialchars($article['titre']); ?>
</a>
</h2>
<p><?php echo nl2br(htmlspecialchars(substr($article['contenu'], 0, 200))); ?>...</p>
</div>
<?php endforeach; ?>
</body>
</html>
Exemple : Vue Article détaillé
<?php ?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo htmlspecialchars($article['titre']); ?></title>
</head>
<body>
<a href="index.php?action=index">← Retour à la liste</a>
<h1><?php echo htmlspecialchars($article['titre']); ?></h1>
<p>
<strong>Auteur :</strong> <?php echo htmlspecialchars($article['auteur']); ?><br>
<strong>Date :</strong> <?php echo date('d/m/Y', strtotime($article['date_creation'])); ?>
</p>
<div>
<?php echo nl2br(htmlspecialchars($article['contenu'])); ?>
</div>
<a href="index.php?action=delete&id=<?php echo $article['id']; ?>"
onclick="return confirm('Supprimer cet article ?')">Supprimer</a>
</body>
</html>
6. Routing simple avec $_GET
Point d'entrée : index.php
<?php
require_once __DIR__ . '/../autoload.php';
require_once __DIR__ . '/../config/database.php';
use Controllers\ArticleController;
use Controllers\HomeController;
$action = $_GET['action'] ?? 'home';
$id = $_GET['id'] ?? null;
try {
switch ($action) {
case 'home':
$controller = new HomeController();
$controller->index();
break;
case 'index':
$controller = new ArticleController($pdo);
$controller->index();
break;
case 'show':
if ($id) {
$controller = new ArticleController($pdo);
$controller->show($id);
}
break;
default:
die("Action inconnue");
}
} catch (\Exception $e) {
error_log($e->getMessage());
die("Une erreur s'est produite");
}
?>
Exemples d'URLs
| URL |
Contrôleur |
Action |
Description |
| index.php |
HomeController |
index() |
Page d'accueil |
| index.php?action=index |
ArticleController |
index() |
Liste des articles |
| index.php?action=show&id=1 |
ArticleController |
show(1) |
Article détaillé |
| index.php?action=create |
ArticleController |
create() |
Créer un article |
Froggiesplaining :
Objectifs de ce cours :
✅ Comprendre le pattern MVC (Modèle-Vue-Contrôleur)
✅ Organiser un projet avec une structure MVC claire
✅ Créer des Modèles pour gérer les données (CRUD)
✅ Créer des Contrôleurs pour orchestrer la logique
✅ Créer des Vues pour afficher les données
✅ Implémenter un système de routing simple
Points clés à retenir :
• Modèle = Ma réserve de mouches (base de données, getAll(), getById())
• Vue = Mon miroir d'eau (HTML/CSS, affichage uniquement)
• Contrôleur = Moi qui décide (reçoit requête, appelle Modèle, passe à Vue)
• Routing = Panneau de signalisation (?action=show&id=5)
• Flux MVC = User → Router → Controller → Model → Controller → View → User
• Séparation des responsabilités = Modèle ne fait pas de HTML, Vue ne fait pas de SQL
• Structure: src/Models/, src/Controllers/, src/Views/
• Point d'entrée unique: public/index.php
Exercice pratique :
1. Créer la structure MVC complète (Models, Controllers, Views)
2. Créer Models\Article.php avec getAll(), getById(), create(), update(), delete()
3. Créer Controllers\ArticleController.php avec index(), show(), create()
4. Créer Views\articles\index.php pour lister les articles
5. Créer Views\articles\show.php pour afficher un article détaillé
6. Créer public\index.php avec switch sur $_GET['action']
7. Tester: index.php?action=index puis index.php?action=show&id=1
8. Conseil de Froggie: Respecte les responsabilités ! Le Modèle ne fait pas de HTML, la Vue ne fait pas de SQL. Chacun sa mare !