Architecture MVC
Présentation
Le Modèle MVC (Modèle-Vue-Contrôleur) est un schéma architectural conçu pour organiser le code d'une application en séparant les responsabilités entre les données, la logique métier et l'affichage. Il est couramment employé dans les applications web structurées.
- Modèle (Model) : Les modèles prennent en charge la logique métier, c'est-à-dire les traitements liés aux données. Un modèle est généralement associé à une table de la base de données, dont il connaît la structure et les contraintes. C'est à ce niveau que sont définies les requêtes permettant de lire, insérer, modifier ou supprimer des enregistrements liés à cette table. Le modèle peut également définir les règles de validation des champs, ainsi que le formatage ou la transformation des données avant leur enregistrement.
- Vue (View) : Les vues s'occupent de la présentation des données. Elles contiennent principalement du code HTML, parfois accompagné de PHP pour insérer des variables ou gérer des conditions et des boucles simples. Leur rôle se limite à l'affichage du contenu transmis par le contrôleur.
- Contrôleur (Controller) : Les contrôleurs servent d'intermédiaires entre les modèles et les vues. Lorsqu'une requête est reçue, le contrôleur correspondant est chargé d'analyser les paramètres, de faire appel aux modèles pour récupérer ou modifier des données, puis de transmettre ces informations à la vue appropriée. Celle-ci peut ensuite construire l'affichage final à partir des données reçues.
Ce découpage en modules distincts permet de mieux structurer le code. Il facilite la maintenance, la réutilisation des composants et le travail en équipe.
Arborescence des fichiers
La structuration des fichiers dans un projet MVC peut varier d'un développeur à l'autre, mais repose sur quelques principes fondamentaux. Il est important d'organiser les fichiers selon leur rôle fonctionnel, en séparant clairement les modèles, les vues et les contrôleurs dans des dossiers distincts. Cette séparation permet de mieux comprendre l'architecture du projet et de faciliter sa maintenance.
Le dossier public/ est utilisé pour stocker les ressources accessibles par le navigateur, comme les fichiers CSS, les images ou les scripts JavaScript. Ce dossier constitue le seul point d'entrée visible depuis l'extérieur.
Les fonctionnalités communes ou transversales, comme un système de gestion des formulaires, peuvent être placées dans un dossier dédié, par exemple core/. Ce dossier regroupe généralement les composants partagés entre plusieurs parties de l'application, comme des classes utilitaires ou des services internes.
Voici un exemple d'arborescence de fichiers dans un projet suivant l'organisation MVC :
📁 projet/
├── 📁 app/
│ ├── 📁 controllers/
│ │ ├── 📁 utilisateur
│ │ │ ├── 📄 ConnexionController.php
│ │ │ ├── 📄 InscriptionController.php
│ │ │ └── 📄 ProfilController.php
│ │ ├── 📄 AccueilController.php
│ │ ├── 📄 ContactController.php
│ │ └── 📄 ErreurController.php
│ ├── 📁 models/
│ │ ├── 📄 ContactModel.php
│ │ └── 📄 UtilisateurModel.php
│ └── 📁 views/
│ ├── 📄 accueil.php
│ ├── 📄 contact.php
│ ├── 📁 erreur/
│ │ └── 📄 404.php
│ ├── 📁 templates/
│ │ ├── 📄 header.php
│ │ └── 📄 footer.php
│ └── 📁 utilisateur/
│ ├── 📄 connexion.php
│ ├── 📄 inscription.php
│ └── 📄 profil.php
├── 📁 config/
│ ├── 📁 lang/
│ │ └── 📁 fr/
│ │ └── 📄 formMessage.json
│ └── 📄 bdd.php
├── 📁 core/
│ ├── 📄 GestionBdd.php
│ ├── 📄 GestionFormulaire.php
│ ├── 📄 GestionMessage.php
│ ├── 📄 GestionAuthentification.php
│ ├── 📄 GestionSession.php
│ ├── 📄 GestionVue.php
│ └── 📄 Routeur.php
├── 📁 public/
│ ├── 📁 css/
│ │ └── 📄 style.css
│ ├── 📁 images/
│ ├── 📁 javascript/
│ ├── 📄 .htaccess
│ ├── 📄 index.php
│ ├── 📄 robots.txt
│ └── 📄 sitemap.xml
└── 📄 .htaccess
Les fichiers des contrôleurs, des modèles et du dossier core/ utilisent la convention PascalCase, c'est-à-dire que chaque mot commence par une majuscule. Cette convention permet d'identifier clairement les fichiers liés à des entités ou des composants logiques importants du projet, et prépare le terrain à une future évolution vers la programmation orientée objet, dans laquelle les classes (qui portent aussi des noms en PascalCase) seront directement définies dans ces fichiers. Cela améliore la lisibilité, la cohérence et la maintenabilité du code, notamment dans des architectures MVC plus robustes ou lors du passage à un framework comme Laravel.
Les Contrôleurs
Dans une architecture MVC (Modèle-Vue-Contrôleur), le contrôleur fait le lien entre les modèles et les vues. Il coordonne les différentes actions à effectuer lorsqu'une requête est reçue.
Lorsqu'une URL est demandée par le navigateur, le routeur l'analyse pour déterminer quel contrôleur doit être exécuté. Ce dernier peut ensuite transmettre les données reçues (par exemple depuis un formulaire) au modèle, ou bien lui demander certaines informations.
Le modèle interagit avec la base de données et renvoie les données demandées au contrôleur. Celui-ci les transmet alors à la vue, qui se charge de les afficher à l'écran.
Ce mécanisme permet de séparer clairement le traitement des données (modèle), la logique d'enchaînement (contrôleur), et l'affichage (vue).
Voici un exemple de contrôleur pour la page de connexion au compte utilisateur :
<?php
// /app/controllers/utilisateur/ConnexionController.php
// Importer le gestionnaire de vues.
require_once dirname(__DIR__, 3) . '/core/GestionVue.php';
// Importer le gestionnaire d'authentification.
require_once dirname(__DIR__, 3) . '/core/GestionAuthentification.php';
// Importer le gestionnaire de formulaires.
require_once dirname(__DIR__, 3) . '/core/GestionFormulaire.php';
// Importer le modèle des utilisateurs.
require_once dirname(__DIR__, 2) . '/Models/UtilisateurModel.php';
// Communiquer les informations nécessaires au bon fonctionnement de la vue.
function obtenirPageInfos(): array
{
return [
'titre' => "Connexion au compte utilisateur",
'description' => "Description de la page de connexion..."
];
}
// Afficher le formulaire de connexion.
// Cette fonction peut être appelée soit par le routeur lors d'une requête GET,
// soit manuellement par la fonction "traiterConnexion()" si le formulaire contient des erreurs.
function afficherConnexion(array $args = []): void
{
afficherVue('utilisateur/connexion', $args);
}
// Traiter la tentative de connexion de l'utilisateur.
// Cette fonction est appelée par le routeur lorsqu'une requête POST est envoyée depuis le formulaire.
function traiterConnexion(): void
{
// Récupérer les règles de validation des champs (utilisateurModel).
$reglesFormConnexion = obtenirReglesFormConnexion();
// Valider les champs envoyés dans $_POST selon ces règles (gestionFormulaire).
// "$validation" est un tableau associatif contenant les éventuelles erreurs
// ainsi que les entrées utilisateur échappées.
$validation = validerChamps($reglesFormConnexion, $_POST);
// Si tout est correct, tenter d'identifier l'utilisateur.
if (empty($erreurs))
{
// Rechercher un utilisateur correspondant à l'email (utilisateurModel).
$utilisateur = selectionnerUtilisateurParSonEmail($_POST['email']);
// Vérifier que l'utilisateur existe et que le mot de passe est correct.
if (!$utilisateur || !password_verify($_POST['mot_de_passe'], $utilisateur['mot_de_passe']))
{
$erreurs['validation'] = "Identifiants incorrects.";
}
else
{
// Si tout est valide, connecter l'utilisateur (gestionAuthentification).
connecterUtilisateur();
// Rediriger vers la page de profil.
header('Location: ' . BASE_URL . '/profil');
// Toujours quitter le script après une redirection.
exit;
}
}
// En cas d'erreur, réafficher le formulaire avec les messages d'erreur et les valeurs échappées.
afficherConnexion($validation);
}
?>
Les Modèles
Dans une architecture MVC (Modèle-Vue-Contrôleur), les modèles gèrent la logique métier. Ils s'occupent de tout ce qui concerne les données : leur récupération, leur traitement, leur validation et, parfois, certains calculs ou tris nécessaires à l'application.
- Manipulation des données : les modèles contiennent les fonctions qui exécutent les requêtes SQL pour lire, insérer, modifier ou supprimer des données dans la base.
- Traitements sur les données : ils peuvent inclure des transformations (par exemple, formater une date ou nettoyer une chaîne) avant ou après leur enregistrement.
- Règles de validation : chaque modèle peut définir les règles que doivent respecter les champs qui lui sont liés (ex. : taille minimale d'un nom, email valide, etc.). Ces règles peuvent ensuite être utilisées par un gestionnaire de formulaire.
- Logique algorithmique : les modèles peuvent intégrer des traitements plus avancés, comme trier des résultats ou appliquer des filtres selon certains critères.
Voici un exemple de modèle destiné à gérer la table des utilisateurs. Il pourra être utilisé par plusieurs contrôleurs, comme connexionController, inscriptionController ou encore profilController.
<?php
// /app/models/UtilisateurModel.php
// Importer le gestionnaire de base de données.
require_once dirname(__DIR__, 3) . '/core/GestionBdd.php';
function obtenirNomTable(): string
{
return 't_utilisateur_uti';
}
function obtenirReglesFormInscription(): array
{
$table = obtenirNomTable();
return [
'pseudo' => [
'requis' => true,
'unique' => [
'table' => $table,
'colonne' => 'uti_pseudo'
],
'minLength' => 2,
'maxLength' => 255
],
'email' => [
'requis' => true,
'unique' => [
'table' => $table,
'colonne' => 'uti_email'
],
'type' => 'email'
],
'mdp' => [
'requis' => true,
'type' => 'password',
'minLength' => 8,
// Pour des raisons de compatibilité avec les futurs algorithmes (ex. Argon2id),
// on autorise jusqu'à 255 caractères. Cependant, avec Bcrypt (algorithme par défaut actuel),
// seuls les 72 premiers caractères sont pris en compte dans le hash.
'maxLength' => 255
]
];
}
function obtenirReglesFormConnexion(): array
{
$table = obtenirNomTable();
return [
'email' => [
'requis' => true,
'type' => 'email'
],
'motDePasse' => [
'requis' => true,
'type' => 'password',
'minLength' => 8,
'maxLength' => 255
]
];
}
/* Toutes les requêtes SQL pour la table Utilisateur */
function creerUtilisateur(string $pseudo, string $email, string $mdp): bool
{
$table = obtenirNomTable();
$requete = "INSERT INTO $table (uti_pseudo, uti_email, uti_motdepasse) VALUES (:pseudo, :email, :motDePasse)";
// Hashage du mot de passe.
$mdp = password_hash($mdp, PASSWORD_DEFAULT);
// Fonction générique présente dans le gestionnaire de base de données (gestionBdd).
return modifierDansTable($requete, [
'pseudo' => $pseudo,
'email' => $email,
'motDePasse' => $mdp
]);
}
function selectionnerUtilisateurParSonEmail(string $email): ?array
{
$table = obtenirNomTable();
$requete = "SELECT * FROM $table WHERE uti_email = :email";
// Fonction générique présente dans le gestionnaire de base de données (gestionBdd).
return selectionnerDansTable($requete, ['email' => $email]);
}
function supprimerUtilisateur(int $id): bool
{
$table = obtenirNomTable();
$requete = "DELETE FROM $table WHERE uti_id = :id";
// Fonction générique présente dans le gestionnaire de base de données (gestionBdd).
return modifierDansTable($requete, ['id' => $id]);
}
?>
Les Vues
Les vues s'occupent de l'interface utilisateur. Elles représentent la partie visible de l'application et sont responsables de l'affichage des données au format HTML, côté client.
Une vue peut contenir du PHP pour insérer dynamiquement du contenu dans la page. Ce mécanisme permet, par exemple, d'afficher un titre, une description, ou encore les données envoyées par le contrôleur, comme le nom d'un utilisateur ou une liste d'articles.
Les vues utilisent généralement des structures de contrôle PHP telles que les boucles (foreach, for) ou les conditions (if, else) pour organiser l'affichage. Elles ne contiennent cependant pas de logique métier (pas de requêtes SQL, de traitement des données, etc.).
Voici un exemple de vue qui affiche la page de connexion :
<!-- /app/views/utilisateur/connexion.php -->
<h1>Connexion</h1>
<form method="post">
<p aria-hidden="true"><span class="alert">*</span> champs obligatoires</p>
<div>
<label for="email">Email</label>
<input
type="text"
name="email"
id="email"
value="<?=$args['valeursEchappees']['email'] ?? ''?>"
required
minlength="2"
maxlength="255">
<?=$args['erreurs']['email'] ?? ''?>
</div>
<div>
<label for="motDePasse">Mot de Passe </label>
<input
type="password"
name="motDePasse"
id="motDePasse"
required
minlength="8"
maxlength="255">
<?=$args['erreurs']['motDePasse'] ?? ''?>
</div>
<div>
<button type="submit">Envoyer</button>
<?=$args['validation'] ?? ''?>
</div>
</form>
<p><a href="<?=BASE_URL?>/inscription">Créer un nouveau compte.</a></p>
?>
Le Routeur d'URL
Le routeur d'URL analyse l'adresse demandée par le navigateur et détermine quelle partie du code doit être exécutée. Il fait le lien entre l'URL entrée par l'utilisateur et les contrôleurs chargés de préparer la réponse. Ce mécanisme évite de devoir créer un fichier .php distinct pour chaque page et permet de centraliser les décisions dans un seul point d'entrée.
Préparer l'environnement pour le routeur
Pour que le routeur prenne la main sur presque toutes les requêtes, il faut que le navigateur n'accède directement qu'au dossier public. Ce dossier contient uniquement les ressources destinées au front-end comme les images, les feuilles de style, les scripts JavaScript et les polices.
Tous les autres fichiers (contrôleurs, vues internes, modèles, configuration) doivent rester invisibles depuis l'extérieur. Sur un hébergement mutualisé, on ne peut pas changer la configuration du serveur pour définir public comme racine réelle du site. On reproduit donc ce fonctionnement avec deux fichiers .htaccess qui coopèrent.
Fichier .htaccess placé à la racine du projet
Ce premier fichier a deux missions importantes. Il rend le dossier public invisible pour l'utilisateur afin d'éviter toute URL contenant /public. Il réécrit ensuite toutes les requêtes vers ce dossier en interne, ce qui laisse l'URL affichée dans la barre du navigateur parfaitement propre.
L'utilisateur peut donc entrer /contact, mais Apache servira en réalité /public/contact. Cette séparation protège l'architecture interne du projet et empêche de créer des chemins incohérents.
# /.htaccess placé à la racine du projet
RewriteEngine On
# Bloque uniquement un accès direct explicite à /public
RewriteCond %{THE_REQUEST} \s/+public(?:[/?\s]|$) [NC]
RewriteRule ^public(?:/.*)?$ - [R=404,L]
# Réécriture interne vers /public
RewriteCond %{REQUEST_URI} !^/public(?:/|$)
RewriteRule ^(.*)$ public/$1 [L]
Fichier .htaccess placé dans le dossier /public
Ce second fichier décide si la requête doit être servie telle quelle (image, CSS, script…) ou envoyée au routeur PHP. Il laisse passer uniquement les fichiers et dossiers réels. Tout ce qui ne correspond à aucun élément du dossier public est réécrit en interne vers index.php.
# /public/.htaccess
RewriteEngine On
# Si la requête ne vise ni un fichier réel ni un dossier réel,
# on l'envoie vers le front controller index.php.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
Les deux fichiers .htaccess n'ont pas le même rôle. Le fichier placé à la racine réécrit d'abord la requête vers le dossier public, sans exposer ce dossier dans l'URL. Le fichier placé dans public prend ensuite le relais : il laisse passer les vraies ressources front-end, puis réécrit les autres requêtes vers index.php, qui devient le point d'entrée unique de l'application.
Exemples pour visualiser la logique
- Ressource front-end /images/logo.png
- L'utilisateur demande /images/logo.png.
- Le fichier .htaccess racine réécrit vers /public/images/logo.png.
- Le .htaccess de public détecte que logo.png existe.
- Le fichier est envoyé immédiatement au navigateur.
- Route métier /article/42
- L'utilisateur demande /article/42.
- Le fichier racine réécrit en interne vers /public/article/42.
- Le .htaccess de public vérifie l'existence du fichier ou dossier correspondant.
- Comme il n'existe pas, Apache exécute index.php.
- Le routeur lit la variable $_GET['url'] pour savoir quoi afficher.
Un premier routeur naïf en PHP
Voici une version minimale d'un routeur. Elle repose sur une série de conditions qui vérifient l'URL et appellent le bon contrôleur. C'est une première étape avant une version plus modulaire et plus évoluée.
<?php
// /public/index.php
// Récupérer la route transmise par le fichier .htaccess du dossier /public.
// Si aucun paramètre "url" n'est présent, on utilise une chaîne vide.
// trim(..., '/') supprime les slashs inutiles au début et à la fin.
// On ajoute ensuite un slash au début pour obtenir une forme uniforme.
// Exemples :
// '' devient '/'
// 'contact' devient '/contact'
// '/contact/' devient '/contact'
$uri = '/' . trim($_GET['url'] ?? '', '/');
// Récupérer la méthode HTTP de la requête en cours.
// Cette information permet au routeur de distinguer, par exemple,
// l'affichage d'une page (GET) du traitement d'un formulaire (POST).
$methode = $_SERVER['REQUEST_METHOD'];
// Construire le chemin vers le dossier des contrôleurs.
$cheminControleurs = dirname(__DIR__) . '/app/controllers/';
if ($uri === '/' && $methode === 'GET')
{
require $cheminControleurs . 'AccueilController.php';
afficherAccueil();
}
else if ($uri === '/contact')
{
require $cheminControleurs . 'ContactController.php';
if ($methode === 'GET')
{
afficherContact();
}
else if ($methode === 'POST')
{
traiterContact();
}
}
else if ($uri === '/connexion')
{
require $cheminControleurs . 'utilisateur/ConnexionController.php';
if ($methode === 'GET')
{
afficherConnexion();
}
else if ($methode === 'POST')
{
traiterConnexion();
}
}
else
{
require $cheminControleurs . 'ErreurController.php';
afficherErreur404();
}
?>
Ce système place toute la logique décisionnelle dans un point d'entrée unique. Le projet devient plus clair, mieux organisé et plus évolutif.
Vers un routeur plus modulaire
Lorsque le projet grandit, il devient plus confortable de remplacer la suite de if par une configuration plus souple basée sur un tableau de routes. Chaque route associe une méthode HTTP, une URL et une fonction à appeler dans le contrôleur correspondant.
<?php
// /public/index.php
// Importer le routeur.
require_once dirname(__DIR__) . '/core/Routeur.php';
// Définir les routes. Chaque entrée correspond à une combinaison
// méthode HTTP + URL + contrôleur + fonction à exécuter.
$routes = [
configurerRoute('GET', '/', 'AccueilController', 'afficherAccueil'),
configurerRoute('GET', '/contact', 'ContactController', 'afficherContact'),
configurerRoute('POST', '/contact', 'ContactController', 'traiterContact'),
configurerRoute('GET', '/connexion', 'utilisateur/ConnexionController', 'afficherConnexion'),
configurerRoute('POST', '/connexion', 'utilisateur/ConnexionController', 'traiterConnexion'),
configurerRoute('GET', '/erreur', 'ErreurController', 'afficherErreur404'),
];
// Lancer le routeur.
demarrerRouteur($routes);
?>
Le schéma ci-dessous illustre le cheminement d'une requête HTTP dans une architecture MVC classique. Il montre comment le fichier .htaccess, le routeur, le contrôleur et le modèle collaborent pour produire une réponse HTML destinée au navigateur.

Définir des routes avec des paramètres personnalisés
Dans un routeur plus avancé, on privilégie des URLs lisibles et descriptives. Plutôt qu'une requête du type mon-site.be/article?id=21, on préfère une structure plus claire comme mon-site.be/article/21.
Pour obtenir ce comportement, certains segments de l'URL sont définis de manière dynamique à l'aide d'accolades, par exemple /article/{id}. Le routeur interprète {id} comme un paramètre variable. Pour contrôler son contenu, on lui associe une expression régulière. Le motif \d+ indique par exemple que ce paramètre doit être un entier.
Cette méthode permet de garantir que seules des URLs valides sont acceptées. Une adresse comme /article/21 est considérée comme correcte, tandis que /article/abc est rejetée et conduit en général à une page d'erreur 404.
<?php
// /public/index.php
// Importer le routeur.
require_once dirname(__DIR__) . '/core/Routeur.php';
// Définir les motifs d'expressions régulières pour les paramètres dynamiques.
// Ici, le paramètre {id} doit contenir uniquement des chiffres.
$patterns = [
'id' => '\d+',
];
// Configurer les routes de l'application.
// Chaque route précise la méthode HTTP, l'URL, le contrôleur et la fonction à appeler.
$routes = [
configurerRoute('GET', '/', 'AccueilController', 'afficherAccueil'),
// Cette route accepte des URLs du type /article/21
// La valeur 21 est extraite et transmise automatiquement à afficherArticle()
configurerRoute('GET', '/article/{id}', 'ArticleController', 'afficherArticle'),
configurerRoute('GET', '/erreur', 'ErreurController', 'afficherErreur404'),
];
// Lancer le routeur avec les routes définies et les motifs de validation.
demarrerRouteur($routes, $patterns);
?>
Dans cette configuration, le fichier Routeur.php se charge de transformer les URLs contenant des paramètres dynamiques en expressions régulières. Il vérifie que chaque segment respecte bien le motif associé dans $patterns, puis extrait les valeurs et les transmet à la fonction du contrôleur correspondante.
Exercices
exo-architecture-mvc (étape 01)
- Objectif : Comprendre le rôle des fichiers .htaccess dans une architecture MVC, en se familiarisant avec les redirections nécessaires pour fixer le dossier public/ comme racine visible du projet, et pour centraliser le traitement des requêtes HTTP via le fichier index.php.
- Instructions :
- Créez un nouveau projet : créez un dossier exo-architecture-mvc-01 dans votre environnement de développement.
- Créez un nouveau dépôt Git local : ajoutez les fichiers, puis effectuez un commit initial.
- Créez la structure de fichiers suivante :
🆕📁 exo-architecture-mvc/ ├── 🆕📁 public/ │ ├── 🆕📁 images/ │ ├── 🆕📄 .htaccess │ └── 🆕📄 index.php └── 🆕📄 .htaccess - Images et accès direct :
- Téléchargez une image et placez-en une copie :
- à la racine du projet
- dans /public/images/
- Vérifier que les deux images sont bien accessibles avec le navigateur :
- /mon-image.jpg
- /public/images/mon-image.jpg
- Téléchargez une image et placez-en une copie :
- Réécrire en interne toutes les requêtes HTTP vers le dossier /public tout en empêchant l'accès direct à ce dossier :
- Ouvrez le fichier .htaccess situé à la racine du projet.
- Configurez-le pour réécrire en interne toutes les requêtes vers le dossier /public, qui deviendra l'unique point d'entrée visible de votre application.
- Ajoutez ensuite une règle qui bloque tout accès direct à une URL commençant par /public, afin que l'utilisateur passe toujours par des URL propres comme /contact plutôt que /public/contact.
- Ressayez ensuite d'accéder aux deux images via votre navigateur :
- /mon-image.jpg : cette image ne sera plus accessible. En effet, elle est située à la racine du projet, or toutes les requêtes sont désormais redirigées vers le dossier /public.
- /public/images/mon-image.jpg : bien que cette image soit située dans un sous-dossier de /public, l'URL contenant explicitement /public provoque une redirection erronée vers /public/public/images/mon-image.jpg, ce qui empêche son affichage.
- Pour accéder correctement aux ressources (images, CSS, JS, etc.), il ne faut plus inclure le segment /public dans les chemins. Utilisez simplement :
- /images/mon-image.jpg
- /css/style.css
- etc.
- Rediriger toutes les requêtes qui ne sont pas des fichiers front-end (images, js, css, etc.) vers le fichier index.php du dossier /public :
- Ouvrez le fichier .htaccess dans le dossier /public.
- Configurez ce fichier pour rediriger toutes les requêtes vers le fichier index.php, sauf si la ressource demandée (image, script, feuille de style…) existe.
- Dans le fichier index.php, ajoutez un simple message, par exemple : echo 'Bonjour depuis index.php'.
- Effectuez les tests suivants dans votre navigateur :
- /mon-image.jpg : Ce fichier n'existe pas à la racine du dossier /public. La redirection vers /public/index.php s'effectue automatiquement, et vous verrez le message s'afficher.
- /images/mon-image.jpg : Ce fichier existe bien dans le dossier /public/images. Le serveur l'affiche directement, sans déclencher la redirection vers index.php.
Pour résumer :
- Le premier fichier .htaccess est placé à la racine du projet. Il redirige toutes les requêtes vers le dossier /public, ce qui permet de définir ce dossier comme racine accessible du site.
- Le second fichier .htaccess, situé dans le dossier /public, redirige toutes les requêtes vers index.php, situé dans ce même dossier, si le fichier ou le dossier demandé n'existe pas.
- Grâce à cette configuration, index.php devient le point d'entrée unique de l'application. Il ne sera exécuté que lorsque la ressource demandée n'est pas trouvée.
- Ce comportement est idéal pour intégrer un routeur d'URL. Le fichier index.php pourra ainsi analyser l'URL et appeler dynamiquement le contrôleur et la méthode correspondants.
exo-architecture-mvc (étape 02)
- Objectif : Comprendre le fonctionnement d'un routeur d'URL simple en PHP, ainsi que le rôle des contrôleurs et des vues, en ajoutant deux pages accessibles via des requêtes GET (une page d'accueil et une page de contact).
- Instructions :
- Réalisez un commit GIT : avant de continuer, n'oubliez pas de sauvegarder l'état actuel de votre projet. Vous pouvez nommer votre commit : "feat: configure .htaccess pour fixer /public comme racine et centraliser les requêtes via index.php".
- Actualisez la structure des fichiers :
📁 exo-architecture-mvc/ ├── 🆕📁 app/ │ ├── 🆕📁 controllers/ │ │ ├── 🆕📄 AccueilController.php │ │ └── 🆕📄 ContactController.php │ └── 🆕📁 views/ │ ├── 🆕📄 accueil.php │ └── 🆕📄 contact.php ├── 📁 public/ │ ├── 📁 images/ │ ├── 📄 .htaccess │ └── 📄 index.php └── 📄 .htaccess - Créez les fonctions des contrôleurs chargées de gérer les requêtes GET vers leur page respective :
- Dans le fichier AccueilController.php, ajoutez une fonction afficherAccueil().
- Dans le fichier ContactController.php, ajoutez une fonction afficherContact().
- Pour l'instant, ces fonctions se contentent d'afficher avec un petit message permettant d'identifiant le contrôleur qui a été appelé par le routeur lors des requêts HTTP.
- Créer un petit routeur dans /public/index.php :
- Commencez par vider le fichier de son contenu.
- Récupérez le segment d'URL sans ses paramètres (ex.: pour mon-domaine.test/contact?abc=123, vous devriez obtenir /contact) :
// Récupérer le chemin demandé dans l'URL (sans paramètres). $uri = '/' . trim($_GET['url'] ?? '', '/'); - À l'aide d'une structure conditionnelle, assurez-vous qu'il s'agit d'une requête GET ($_SERVER['REQUEST_METHOD']) et comparez ce segment d'URL pour vérifier s'il correspond à la page d'accueil (/) ou à la page de contact (/contact) pour inclure dynamiquement le contrôleur correspondant et la fonction chargée de traiter les requêtes GET de la page :
- / : Inclure le fichier AccueilController.php et appeler la fonction afficherAccueil().
- /contact : Inclure le fichier ContactController.php et appeler la fonction afficherContact().
- Testez les deux chemins dans la barre d'URL du navigateur pour vous assurer que les bons contrôleurs sont appelés.
- Les fonctions des contrôleurs ne sont pas censées contenir directement le code HTML. Leur rôle est de décider quelle vue afficher, pas d'afficher elles-mêmes le contenu.
- Modifiez le contenu des fonctions afficherAccueil() et afficherContact() des contrôleurs pour qu'elles incluent simplement la vue correspondante à l'aide de require_once.
- Dans chaque vue (/views/accueil.php et /views/contact.php), ajoutez un peu de HTML, par exemple un <h1> affichant le nom de la page.
- Testez les deux pages dans le navigateur pour vérifier que le routeur appelle toujours bien les bons contrôleurs en fonction du segment d'URL de la requête HTTP, et que chaque contrôleur déclenche l'affichage de la vue correspondante.
exo-architecture-mvc (étape 03)
- Objectif : Structurer proprement l'affichage des pages en mettant en place un système de templates (en-tête et pied de page) et un gestionnaire de vues permettant aux contrôleurs de déléguer l'affichage de manière centralisée.
- Instructions :
- Réalisez un commit GIT : avant de continuer, n'oubliez pas de sauvegarder l'état actuel de votre projet. Vous pouvez nommer votre commit : "feat: implémente routeur d'URL basique et pages accueil/contact (contrôleurs/vues)".
- Actualisez la structure des fichiers :
📁 exo-architecture-mvc/ ├── 📁 app/ │ ├── 📁 controllers/ │ │ ├── 📄 AccueilController.php │ │ └── 📄 ContactController.php │ └── 📁 views/ │ ├── 📄 accueil.php │ ├── 📄 contact.php │ └── 🆕📁 templates/ │ ├── 🆕📄 footer.php │ └── 🆕📄 header.php ├── 🆕📁 core/ │ └── 🆕📄 GestionVue.php ├── 📁 public/ │ ├── 📁 images/ │ ├── 📄 .htaccess │ └── 📄 index.php └── 📄 .htaccess - Créer un système de templates :
- Complétez les fichiers header.php et footer.php avec le contenu classique attendu dans ce type de fichiers. Le fichier header.php doit contenir les balises d'ouverture <!DOCTYPE html>, <html>, <head> et <body>, tandis que le fichier footer.php doit contenir les balises de fermeture </body> et </html>.
- Dans header.php, ajoutez également l'affichage de variables PHP pour les valeurs spécifiques à chaque page (ex.: <title>, <meta name="description">, etc.). Ces valeurs seront fournies par le contrôleur spécifique à la page via un tableau associatif nommé $args (ex.: $args['titre'], $args['description'], etc.).
- Actualiser les contrôleurs :
- Ajoutez une nouvelle fonction obtenirInfosPage() dans chaque contrôleur. Cette fonction doit retourner un tableau associatif contenant les chaînes de caractères utiles au template. Par exemple :
function obtenirInfosPage(): array { return [ 'titre' => 'Titre de la page...', 'description' => 'Description du contenu de la page...' ]; } - Au début de la fonction dont l'objectif est d'afficher la vue (afficherAccueil() et afficherContact()), appelez obtenirInfosPage() et stockez son résultat dans une variable $args.
- Incluez ensuite le fichier header.php, en veillant à ce qu'il ait accès à la variable $args.
- Incluez la vue de la page concernée.
- Terminez par l'inclusion du fichier footer.php.
- Testez les deux pages dans le navigateur pour vous assurer que le template fonctionne correctement. N'oubliez pas de vériier si les balises <title> et <meta name="description"> ont bien été compétées avec les valeurs du tableau associatif $args communiqué par le contrôleur de la page.
- Ajoutez une nouvelle fonction obtenirInfosPage() dans chaque contrôleur. Cette fonction doit retourner un tableau associatif contenant les chaînes de caractères utiles au template. Par exemple :
- Créer un gestionnaire de vues :
- Dans le fichier /core/GestionVue.php, créez une fonction afficherVue(string $nomVue, array $args).
- Cette fonction doit :
- Recevoir le nom de la vue à afficher (ex. : 'accueil.php' ou 'contact.php').
- Recevoir un tableau associatif $args contenant des données à transmettre à la vue (titre, description, données issues d'une base, etc.).
- Inclure successivement les fichiers header.php, la vue demandée, puis footer.php.
- Dans les contrôleurs, incluez la dépendance GestionVue.php et actualisez les fonctions afficherAccueil() et afficherContact() pour remplacer les instructions concernant l'inclusion des fichiers liées au template et à la vue par l'appel de la fonction afficherVue(), en lui transmettant le nom de la vue et les données nécessaires via $args.
- Testez à nouveau les pages accueil et contact dans le navigateur pour vous assurer que tout s'affiche correctement avec l'ajout du gestionnaire de vues.
exo-architecture-mvc (étape 04)
- Objectif : Comprendre l'un des nombreux avantages de la redirection des requêtes vers un point d'entrée unique, le fichier index.php situé dans le dossier /public. Cette approche permet notamment d'appliquer une dépendance, comme un système de gestion des sessions, à toutes les pages en l'important une seule fois, au point d'entrée.
-
Instructions :
- Réalisez un commit Git : Avant de poursuivre, sauvegardez l'état actuel de votre projet. Vous pouvez utiliser le message de commit suivant : "feat: implémente système de templates et gestionnaire de vues centralisé".
-
Actualisez la structure des fichiers :
📁 exo-architecture-mvc/ ├── 📁 app/ │ ├── 📁 controllers/ │ │ ├── 📄 AccueilController.php │ │ └── 📄 ContactController.php │ └── 📁 views/ │ ├── 📄 accueil.php │ ├── 📄 contact.php │ └── 📁 templates/ │ ├── 📄 footer.php │ └── 📄 header.php ├── 📁 core/ │ ├── 🆕📄 GestionSession.php │ └── 📄 GestionVue.php ├── 📁 public/ │ ├── 📁 images/ │ ├── 📄 .htaccess │ └── 📄 index.php └── 📄 .htaccess -
Créez un gestionnaire de session centralisé :
- Créez un nouveau fichier nommé GestionSession.php dans le dossier /core.
- Dans ce fichier, définissez une fonction initialiserSession() dont le rôle sera de démarrer la session pour la requête HTTP actuelle. Afin de réaliser une configuration de session qui respecte les bonnes pratiques de sécurité, n'hésitez pas à vous référer au chapitre dédié aux cookies et aux variables de session.
- Ce fichier pourra par la suite contenir d'autres fonctions liées à la gestion des sessions (comme la destruction complète de la session), mais pour cet exercice, la fonction précédente suffira.
- Dans le fichier index.php situé à la racine du dossier /public, incluez votre gestionnaire de session et appelez la fonction initialiserSession(). Ce fichier étant le point d'entrée unique du script (le fichier .htaccess redirige toutes les requêtes qui ne sont pas des ressources client vers ce fichier), la session sera ainsi appliquée sur toutes les pages.
exo-architecture-mvc (étape 05)
- Objectif : Vous familiariser avec la logique de traitement des entrées utilisateur au sein des contrôleurs, en se basant sur la soumission du formulaire de la page de contact.
-
Instructions :
- Réalisez un commit Git : Avant de continuer, sauvegardez l'état actuel de votre projet. Vous pouvez utiliser le message de commit suivant : "feat: ajoute gestionnaire de sessions centralisé via index.php".
-
Actualisez la structure des fichiers :
📁 exo-architecture-mvc/ ├── 📁 app/ │ ├── 📁 controllers/ │ │ ├── 📄 AccueilController.php │ │ └── 📄 ContactController.php │ ├── 🆕📁 models/ │ │ └── 🆕📄 ContactModel.php *1 │ └── 📁 views/ │ ├── 📄 accueil.php │ ├── 📄 contact.php │ └── 📁 templates/ │ ├── 📄 footer.php │ └── 📄 header.php ├── 📁 core/ │ ├── 🆕📄 GestionForm.php *1 │ ├── 📄 GestionSession.php │ └── 📄 GestionVue.php ├── 📁 public/ │ ├── 📁 images/ │ ├── 📄 .htaccess │ └── 📄 index.php └── 📄 .htaccess *1 : Uniquement si vous optez pour le gestionnaire de formulaire générique (GestionForm). -
Ajoutez le formulaire de la page contact : Dans la vue contact.php, insérez un formulaire de contact (avec la méthode POST) incluant les champs suivants avec leurs attributs de validation HTML5 :
- nom :
- Champ requis (required).
- Minimum 2 caractères (minlength="2").
- Maximum 255 caractères (maxlength="255").
- prénom :
- Champ facultatif.
- Minimum 2 caractères (minlength="2").
- Maximum 255 caractères (maxlength="255").
- email :
- Champ requis (required).
- Type email (type="email").
- message :
- Champ requis (required).
- Minimum 10 caractères (minlength="10").
- Maximum 3000 caractères (maxlength="3000").
- nom :
- Actualisez le routeur : Dans votre routeur principal (index.php), ajoutez une nouvelle règle pour gérer les requêtes POST sur la route /contact. Cette règle doit exécuter la fonction traiterContactForm() du contrôleur de contact.
- Actualisez le contrôleur ContactController.php :
- Créez une nouvelle fonctions traiterContactForm() au sein de ce contrôleur. Cette fonction aura pour objectif de valider les entrées utilisateur, de stocker les éventuels messages d'erreur liés à ces entrées, et d'envoyer le courriel à l'administrateur si les données sont valides. Pour respecter le principe de responsabilité unique (Single Responsibility Principle - SRP), cette fonction délèguera ces tâches spécifiques en appelant d'autres modules ou fonctions spécialisées.
- Pour valider les entrées utilisateur, deux approches sont possibles :
- L'approche recommandée et la plus flexible consiste à créer un gestionnaire de formulaire générique (par exemple, dans le dossier /core). Ce gestionnaire contiendrait :
- Des fonctions spécifiques pour chaque règle de validation (ex. : respecteLongueurMin(), estValideEmail(), etc.).
- Une fonction principale qui prendrait en arguments les entrées utilisateur (comme $_POST) et un tableau associatif des règles de validation pour chaque champ (où chaque clé serait le nom du champ et sa valeur un tableau de règles). Cette fonction parcourrait ce tableau pour confronter chaque règle définie à l'entrée utilisateur correspondante dans $_POST, afin de s'assurer de sa validité.
- Pour organiser les règles de validation de manière propre et réutilisable, il est recommandé de créer un modèle dédié au formulaire de contact ContactModel.php dans le dossier /app/models. Dans ce modèle, définissez une fonction obtenirReglesContactForm(). Cette fonction aura pour unique rôle de retourner le tableau associatif contenant toutes les règles de validation pour les champs de votre formulaire.
- Cependant, pour simplifier cet exercice, vous pouvez aussi opter pour une approche plus directe mais moins réutilisable en créant une simple fonction validerContactForm() au sein du contrôleur de la page contact dédiée uniquement à la validation des champs de ce formulaire spécifique, en utilisant une structure conditionnelle (if/else) pour vérifier les règles.
- L'approche recommandée et la plus flexible consiste à créer un gestionnaire de formulaire générique (par exemple, dans le dossier /core). Ce gestionnaire contiendrait :
- Dans les deux approches de validation (que vous utilisiez un gestionnaire générique ou une fonction dédiée dans le contrôleur), la fonction chargée de valider les entrées utilisateur devra stocker un tableau associatif contenant les messages déstinés au formualire dans une variable de session (par exemple, $_SESSION['erreurs']). Les clés de ce tableau correspondront aux noms des champs du formulaire pour faciliter leur exploitation lors de leur affichage sous les champs respectifs.
- Si le tableau contient des erreurs, il est utile de stocker les données saisies par l'utilisateur dans une variable de session (par exemple $_SESSION['entreesUtilisateur'] = $_POST) afin de pouvoir les réafficher automatiquement dans les champs du formulaire après la redirection.
- Réagissez au résultat de la validation des entrées utilisateur : Au sein de la fonction traiterContactForm() dans votre ContactController :
- Après la validation, vérifiez si le tableau des erreurs est vide. Si c'est le cas, cela signifie que toutes les entrées utilisateur sont valides. Cette vérification nous permettra d'envoyer un courriel à l'administrateur lors de l'exercice suivant. Pour le moment, vous n'avez aucune action spécifique à implémenter ici.
- Implémentez le PRG (Post Redirect Get) : Redirigez l'utilisateur vers la même page en utilisant une requête GET afin de prévenir la resoumission accidentelle de données (par exemple, en cas de rechargement de page ou de navigation arrière) :
// Effectue une redirection PRG (Post-Redirect-Get) : // Le code HTTP 303 ("See Other") force le navigateur à utiliser une requête GET pour la redirection, // ce qui prévient la resoumission accidentelle du formulaire. header("Location: " . $_SERVER['REQUEST_URI'], true, 303); // N'oubliez pas d'appeler exit() après une redirection. exit();
- Actualiser la fonction afficherContact() :
- Suite à la redirection PRG, c'est la fonction afficherContact() (méthode GET sur /contact) qui sera appelée. Il faut donc actualiser cette fonction pour qu'elle puisse exploiter le tableau des erreurs au formulaire $_SESSION['erreurs'] si celui-ci est disponible.
- Juste après l'initialisation de la variable $args, afin de pouvoir transmettre les messages d'erreurs et les entrées utilisateur à la vue :
- Récupérez les messages stockés dans $_SESSION['erreurs']. Ajoutez ces messages à la variable $args avec une clé similiare à l'originale $args['erreurs'] = $_SESSION['erreurs'].
- Une fois les erreurs récupérées et ajoutées à $args, supprimez les messages d'erreur de la variable de session (unset($_SESSION['erreurs'])) pour éviter qu'ils ne s'affichent à chaque rechargement de la page.
- Faites de même pour les éventuelles entrées utilisateur qu'il faudrait réafficher en cas d'erreur $args['entreesUtilisateur'] = $_SESSION['entreesUtilisateur'] et ensuite unset($_SESSION['entreesUtilisateur']).
- Comme la variable $args est transmise à la vue via l'appel à afficherVue(), les messages d'erreur ainsi que les entrées utilisateurs seront accessibles dans la vue pour être affichés sous leurs champs respectifs.
- Actualiser la vue contact pour afficher les éventuels messages d'erreur ainsi qu'en réaffichant les entrées utilisateur en cas d'erreur en exploitant les tableaux $args['erreurs'] et $args['entreesUtilisateur'], transmis par le contrôleur, pour :
- afficher les messages d'erreur sous les champs de formulaire correspondants. Par exemple, pour afficher l'erreur liée au champ "message", vous pourriez utiliser : $args['erreurs']['message'] ?? ''.
- Faites de même pour réafficher les entrées utilisateur en cas d'erreur. Par exemple, pour réafficher l'entrée utilisateur du champ "message", vous pourriez utiliser : $args['entreesUtilisateur']['message'] ?? ''.
- Affichez un message de statut général pour informer l'utilisateur :
- Si le tableau $args['erreurs'] existe et est vide, cela indique une soumission réussie. Affichez un message comme : "Votre message a été envoyé avec succès !".
- Si le tableau $args['erreurs'] existe mais n'est pas vide, des erreurs de validation ont été détectées. Affichez un message comme : "Une ou plusieurs erreurs sont survenues. Veuillez corriger les champs indiqués."
- Si $args['erreurs'] n'existe pas, cela signifie qu'aucune soumission de formulaire n'a eu lieu. Dans ce cas, n'affichez aucun message général.
- Important : N'oubliez pas d'échapper les données utilisateurs (avec htmlspecialchars()) lors de leur affichage, pour éviter les attaques de type XSS.
exo-architecture-mvc (étape 06)
- Objectif : Se familiariser davantage avec les différentes responsabilités qu'un contrôleur peut assumer, notamment en mettant en place un gestionnaire de courriel réutilisable pour traiter l'envoi de messages depuis le formulaire de contact lorsque les données sont valides.
- Instructions :
- Réalisez un commit GIT : avant de continuer, n'oubliez pas de sauvegarder l'état actuel de votre projet. Vous pouvez nommer votre commit : "feat: ajoute validation des entrées utilisateur pour le formulaire de la page contact".
- Ajoutez une fonction permettant de gérer l'envoi du courriel au propriétaire du site dans le contrôleur de la page contact :
- Dans le fichier ContactController.php, créez une fonction nommée envoyerCourrielFormContact(). Elle sera dédiée à l'envoi du message issu du formulaire de contact :
- Elle prendra comme unique argument le tableau des données utilisateur ($_POST).
- Elle formatera un message HTML (type text/html) sous forme de liste contenant le prénom, le nom, l'e-mail et le message de l'utilisateur.
- Elle préparera les entêtes dans un tableau associatif avec :
- 'MIME-Version' : "1.0"
- 'Content-Type' : "text/html; charset=UTF-8"
- 'Content-Transfer-Encoding' : "quoted-printable"
- 'From' : une adresse technique comme bot@mon-domaine.test
- Le courriel sera envoyé à une adresse fixe de l'administrateur, avec un sujet explicite comme : "Message du formulaire de contact".
- Elle utilisera la fonction PHP mail() pour l'envoi, et retournera true si l'opération a réussi, false sinon.
- Un système de try/catch pourrait être envisagé pour capturer d'éventuelles erreurs, mais ce n'est pas nécessaire ici.
- Pour rester simple dans le cadre de cet exercice de découverte de l'architecture MVC, cette fonction est intégrée directement dans le contrôleur. Idéalement, on créerait plutôt une fonction plus générique et réutilisable, placée dans un gestionnaire dédié (comme /core/GestionCourriel.php), mais ce serait inutilement complexe à ce stade.
- Dans le fichier ContactController.php, créez une fonction nommée envoyerCourrielFormContact(). Elle sera dédiée à l'envoi du message issu du formulaire de contact :
- Utilisez la fonction d'envoi de courriel dans la fonction traiterContactForm() du contrôleur de la page contact : Si les entrées utilisateur provenant du formulaire de contact ont été validées, appelez la fonction envoyerCourrielFormContact() en lui transmettant ces données ($_POST). Cette action doit être réalisée juste avant d'appliquer la redirection PRG détaillée précédemment.
exo-architecture-mvc (étape 07)
- Objectif : Comprendre le rôle principal d'un modèle en l'utilisant pour interagir avec une table spécifique, ici dans le cadre de l'ajout d'une zone de commentaires anonymes sur la page d'accueil.
- Instructions :
- Réalisez un commit GIT : avant de continuer, n'oubliez pas de sauvegarder l'état actuel de votre projet. Vous pouvez nommer votre commit : feat: ajoute envoi du courriel après validation du formulaire de contact.
- Actualisez la structure des fichiers :
📁 exo-architecture-mvc/ ├── 📁 app/ │ ├── 📁 controllers/ │ │ ├── 📄 AccueilController.php │ │ └── 📄 ContactController.php │ ├── 📁 models/ │ │ ├── 📄 ContactModel.php │ │ └── 🆕📄 CommentaireModel.php │ └── 📁 views/ │ ├── 📄 accueil.php │ ├── 📄 contact.php │ └── 📁 templates/ │ ├── 📄 footer.php │ └── 📄 header.php ├── 🆕📁 config/ │ └── 🆕📄 bdd.php ├── 📁 core/ │ ├── 🆕📄 GestionBdd.php │ ├── 📄 GestionForm.php │ ├── 📄 GestionSession.php │ └── 📄 GestionVue.php ├── 📁 public/ │ ├── 📁 images/ │ ├── 📄 .htaccess │ └── 📄 index.php └── 📄 .htaccess - Créez la base de données et la table de commentaires : Dans phpMyAdmin (ou équivalent), exécutez le SQL suivant :
-- Crée la base de données (si elle n'existe pas encore) CREATE DATABASE IF NOT EXISTS bdd_architecture_mvc CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- Utilise la base de données USE bdd_architecture_mvc; -- Crée la table des commentaires anonymes CREATE TABLE t_commentaires_com ( id INT AUTO_INCREMENT PRIMARY KEY, contenu TEXT NOT NULL, date_creation DATETIME DEFAULT CURRENT_TIMESTAMP ); - Ajoutez le formulaire des commentaires de la page d'accueil : Dans la vue accueil.php, insérez un formulaire (avec la méthode POST) incluant le champ suivant avec ses attributs de validation HTML5 :
- contenu :
- Champ requis (required).
- Minimum 10 caractères (minlength="10").
- Maximum 3000 caractères (maxlength="3000").
- contenu :
- Actualisez le routeur : Dans /public/index.php, ajoutez une nouvelle route POST pour / qui déclenchera la fonction traiterCommentaireForm() du contrôleur AccueilController.
- Créez un fichier de configuration de la base de données : Dans /config/bdd.php, créez une fonction obtenirInfoDeConnexionBDD() qui retourne un tableau associatif avec les clés suivantes :
- 'serveur' (ex : 'localhost')
- 'bdd' (ex : 'bdd_architecture_mvc')
- 'utilisateur' (ex : 'root')
- 'mdp' (ex : '')
- Créez une fonction de connexion à la base de données : Dans /core/GestionBdd.php, créez la fonction obtenirConnexionPDO() :
- Cette fonction inclura le fichier /config/bdd.php.
- Elle utilisera les infos de connexion pour créer un objet PDO correctement configuré (UTF-8, gestion d'erreurs avec exceptions, etc.).
- Elle retournera cet objet PDO pour être utilisé dans les modèles.
- Créez le modèle CommentaireModel.php dans /app/models :
- Le rôle de ce modèle sera d'accueillir les différentes fonctions permettant de communiquer avec la table t_commentaire_com de la base de données. De plus, si vous avez opté pour le développement d'un gestionnaire de formulaire flexible (/core/GestionForm), ce modèle devra aussi fournir un tableau contenant les règles des champs du formulaire commentaire (comme vous l'auriez fait dans le modèle de la page contact).
- Ajoutez une fonction recupererCommentaires().
- Cette fonction récupérera une connexion PDO via obtenirConnexionPDO(), puis récupérer toutes les colonnes de tous les commentaires stockés dans la table commentaires.
- Ajoutez une fonction ajouterCommentaire(string $contenu).
- Cette fonction récupérera une connexion PDO via obtenirConnexionPDO(), puis insérera le contenu dans la table commentaires.
- Important : N'oubliez pas d'utiliser une requête préparée pour éviter les injections SQL.
- Ajoutez la fonction traiterCommentaireForm() dans AccueilController.php :
- Récupérez le champ contenu dans $_POST.
- Validez manuellement les données (ou utilisez GestionForm si vous l'avez mis en place à l'étape 5).
- Stocker les erreurs et les entrées utilisateur dans les variables de session pour qu'elles soient toujours disponibles après le PRG ($_SESSION['erreurs'] et $_SESSION['entreesUtilisateur']) comme vous l'aviez fait pour le formulaire de contact.
- Si valide, appelez ajouterCommentaire() pour insérer le commentaire.
- Appliquez ensuite le principe de redirection PRG, tel qu'expliqué dans l'exercice exo-architecture-mvc (étape 05).
- Remarque : vous pouvez soit réutiliser le gestionnaire de formulaire générique créé précédemment, soit faire une validation directe comme dans le contrôleur de contact. Les deux approches sont valides.
- Actualiser la fonction afficherAccueil() dans AccueilController.php :
- Récupérer les éventuels messages déstinés au formulaire :
- Suite à la redirection PRG, c'est la fonction afficherAccueil() (méthode GET sur /) qui sera appelée. Il faut donc actualiser cette fonction pour qu'elle puisse exploiter le tableau des messages déstinés au formulaire $_SESSION['erreurs'] si celui-ci est disponible.
- Juste après l'initialisation de la variable $args dans cette fonction, récupérez les messages d'erreur stockés dans $_SESSION['erreurs']. Ajoutez ces messages à la variable $args avec une clé similiare à l'originale $args['erreurs'] = $_SESSION['erreurs'].
- Une fois les erreurs récupérées et ajoutées à $args, supprimez la clé erreurs de la variable de session (unset($_SESSION['erreurs'])) pour éviter qu'elles ne s'affichent à chaque rechargement de la page.
- Faites de même pour les entrées utilisateur en cas d'erreur ($args['entreesUtilisateur'] = $_SESSION['entreesUtilisateur'] ensuite unset($_SESSION['entreesUtilisateur'])).
- Récupérer les éventuels commentaires : utiliser la fonction recupererCommentaires() du modèle CommentaireModel.php, puis stocker le tableau retourné dans $args['commentaires'].
- Comme la variable $args est transmise à la vue via l'appel à afficherVue(), les messages d'erreur, les entrées utilisateur ainsi que les commentaires seront accessibles dans la vue pour être affichés.
- Récupérer les éventuels messages déstinés au formulaire :
- Actualiser la vue de la page d'accueil (accueil.php) :
- Afficher les éventuels messages d'erreur, les entrées utilisateur en cas d'erreur ainsi que le message de validation du formulaire, exploitez les tableaux $args['erreurs'] et $args['entreesUtilisteur'] :
- Affichez les éventuels messages d'erreur juste sous le champ concerné (par exemple avec $args['erreurs']['contenu'] ?? '').
- Faites de même pour réafficher les entrées utilisateur en cas d'erreur. Par exemple, pour réafficher l'entrée utilisateur du champ "message", vous pourriez utiliser : $args['entreesUtilisateur']['contenu'] ?? ''.
- Affichez un message général de validation : s'il n'y a aucune erreur, affichez un message de succès. Dans le cas contraire, affichez un message d'échec.
- Afficher les commentaires :
- Parcourez le tableau $args['commentaires'] à l'aide d'une boucle foreach.
- Pour chaque commentaire, affichez le contenu ainsi que sa date de création, formatée sous la forme jour/mois/année à heure:minutes (ex.: 12/06/2025 à 14:45).
- Pour manipuler la date récupérée depuis la base (au format SQL), vous utiliserez les fonctions prédéfinies suivantes :
- strtotime() : convertit une chaîne de date/heure issue de la base de données en timestamp Unix, c'est-à-dire le nombre de secondes écoulées depuis le 1er janvier 1970 à 00:00:00 UTC (ex.:"2025-06-12 14:45:00" est converti en 1755038700).
- date() : formate ce timestamp en une date lisible, selon le format souhaité (ex.: date('d/m/Y à H:i', $timestampUnixQuiVaEtreFormate)).
- N'hésitez pas à consulter la documentation officielle de PHP pour approfondir vos connaissances, ou à effectuer des recherches sur les différentes sources fiables disponibles en ligne.
- Important : N'oubliez pas d'échapper les données utilisateurs (avec htmlspecialchars()) lors de leur affichage, pour éviter les attaques de type XSS.
- Afficher les éventuels messages d'erreur, les entrées utilisateur en cas d'erreur ainsi que le message de validation du formulaire, exploitez les tableaux $args['erreurs'] et $args['entreesUtilisteur'] :
Exercice : Projet Progressif 07
Bonus (facultatif)
- Objectif : Structurer le projet en appliquant les principes de l'architecture MVC (Modèle - Vue - Contrôleur).
- Conseils :
- Respectez les rôles :
- Modèles : ils contiennent la logique métier, c'est-à-dire tout ce qui touche principalement à la communication avec la base de données (requêtes SQL), mais aussi le formatage des données et, si besoin, certaines règles de validation des champs d'un formulaire.
- Contrôleurs : ils jouent le rôle d'un chef d'orchestre. Ils reçoivent les actions de l'utilisateur (par exemple : la soumission d'un formulaire), appellent les fonctions du modèle si nécessaire (recherche en base, insertion, etc.), peuvent également solliciter des modules ou services externes (ex. : envoi d'email, génération de jeton), puis transmettent les résultats à la vue pour affichage.
- Vues : elles reçoivent les données du contrôleur et se chargent uniquement de l'affichage. Elles ne doivent jamais contenir de logique métier ni de requête SQL.
- Organisez vos dossiers :
- /app/controllers/ pour les contrôleurs
- /app/models/ pour les fonctions métiers
- /app/vues/ pour les fichiers HTML
- /core/ pour les composants partagés (ex. : gestionnaire de vues, fonctions utilitaires)
- /public/ comme seul dossier accessible depuis le navigateur (point d'entrée unique)
- Un contrôleur par fonctionnalité :
- AccueilController.php : pour la page d'accueil
- ContactController.php : pour la page de contact
- InscriptionController.php : pour la page d'inscription
- ...
- Un modèle pour chaque entité : Par exemple : un modèle UtilisateurModel.php pour la table t_utilisateur_uti (pour tout ce qui concerne l'inscription, la connexion et les données du profil).
- Centralisez la gestion de l'affichage : utilisez une fonction comme afficherVue() pour inclure automatiquement l'en-tête et le pied de page autour du contenu principal. Cette fonction est appelée par le contrôleur, une fois que celui-ci a récupéré et préparé les données nécessaires à la vue.
- Commencez simple : Faites d'abord un affichage statique, puis ajoutez progressivement la logique dynamique (formulaire, session, etc.).
- Testez chaque fonctionnalité séparément : commencez par faire fonctionner chaque page indépendamment (comme la page de contact, d'inscription ou de connexion), sans encore vous soucier du reste du site. Par exemple, assurez-vous que le formulaire d'inscription insère bien un nouvel utilisateur en base avant de passer à la connexion. Cela permet de corriger plus facilement les erreurs et de progresser étape par étape.
- Versionnez votre progression : utilisez Git pour enregistrer les étapes clés de votre travail. Cela vous aidera à revenir en arrière si besoin.
- Respectez les rôles :