TD9 – Messages Flash Une autre application des sessions
Ajouter des messages à une vue
Bandeau de messages
Au lieu de créer une vue spécifique comme utilisateurCree.php pour afficher un
message au-dessus d’une vue existante, nous allons intégrer à toutes nos pages un
bandeau de messages, appelés messages flash.
Les messages pourront être de 4 types : success (vert), info (bleu), warning (jaune) et danger (rouge). Chaque type peut comporter plusieurs messages. Voici 2 exemples dans le cas d’un site qui gèrerait aussi des voitures :


Pour stocker les messages flash, nous utiliserons des tableaux de tableaux de
string, par exemple :
$messagesFlash = [
"success" => [
0 => "Message succès"
],
"danger" => [
0 => "Message danger 1",
1 => "Message danger 2"
]
];
-
Copiez/collez dans un nouveau dossier
TD9tous les fichiers du dossierTD8. -
Les messages peuvent s’afficher sur n’importe quelle page qui utilise
vueGenerale.php, que nous allons mettre à jour pour afficher tous les messages existants.Dans
vueGenerale.php, rajoutez le<div>suivant juste après le<nav>dans le<header>.<div> <?php /** @var string[][] $messagesFlash */ foreach($messagesFlash as $type => $messagesFlashPourUnType) { // $type est l'une des valeurs suivantes : "success", "info", "warning", "danger" // $messagesFlashPourUnType est la liste des messages flash d'un type foreach ($messagesFlashPourUnType as $messageFlash) { echo <<< HTML <div class="alert alert-$type"> $messageFlash </div> HTML; } } ?> </div>Prenez le temps de bien comprendre ce code.
- Dans
ControleurGenerique::afficherVue(), récupérez les messages dans une variable$messagesFlashà partir de l’URL (s’ils y sont présents), juste avant lerequirede la vue :// Avec le if/else ternaire $messagesFlash = isset($_REQUEST["messagesFlash"]) ? $_REQUEST["messagesFlash"] : []; // Ou de manière équivalente avec l'opérateur "null coalescent" // https://www.php.net/manual/fr/migration70.new-features.php#migration70.new-features.null-coalesce-op // $messagesFlash = $_REQUEST["messagesFlash"] ?? []; - Pour le CSS des messages d’alerte, rajoutez le code suivant à
navstyle.css. Le code provient du composant d’alerte du framework CSS Bootstrap :/* Bootstrap alerts */ /* https://getbootstrap.com/docs/3.4/components/#alerts */ .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; }On obtient alors par exemple un bloc de succès avec le code :
<div class="alert alert-success">...</div> - Testez les messages flash. Pour cela, appelez une vue en rajoutant le tableau des
messages dans le query string de la manière suivante :
http://localhost/tds-php/TD9/web/controleurFrontal.php?messagesFlash[success][]=Hello+Worldou
http://localhost/tds-php/TD9/web/controleurFrontal.php?messagesFlash[danger][]=Message+de+danger+1&messagesFlash[danger][]=Message+de+danger+2Remarque : les messages sont affichés sans
htmlspecialchars(cf. TD5). Comme ils proviennent ici de l’URL, n’importe qui peut injecter du HTML ou du JavaScript dans la page (essayez avecmessagesFlash[info][]=<h1>Hack</h1>). Cette faille disparaîtra dans la suite du TD, quand les messages flash seront écrits uniquement par le serveur.
Redirection
Il est pratique pour le site de pouvoir rediriger vers une autre page en cas d’erreur ou de succès. Par exemple, si le site gère les voitures et que le client est sur le formulaire de création d’une voiture :

S’il saisit une immatriculation existante, le site le redirige vers le formulaire de création avec un message flash d’avertissement :

De même, s’il oublie un champ du formulaire (ce qui ne devrait normalement pas
arriver puisque les <input> ont l’attribut required), le site le redirige vers le
formulaire de création avec un message flash de danger :

Quand le formulaire est valide, le client est redirigé vers la vue qui liste toutes les voitures avec un message flash de succès :

Le système de redirection est pratique car il évite la duplication de code.
Précédemment, pour que l’action creerDepuisFormulaire affiche la liste des
voitures en cas de succès, il fallait récupérer la liste des voitures et appeler
la vue voitures/liste.php. Ce code existe déjà dans l’action afficherListe.
À la place, nous allons juste rediriger le client vers l’URL correspondant à
l’action afficherListe sans code supplémentaire.
- Pour la redirection, nous allons utiliser le code suivant (à encapsuler dans
une méthode statique
redirigerVersURL(string $url): neverdu contrôleur générique) :header("Location: $url"); exit();En effet, quand un navigateur reçoit l’en-tête de réponse
Location, il effectue une redirection, c’est-à-dire qu’il lance une requête vers la nouvelle URL comme si on avait cliqué sur un lien. Enfin,exit()termine l’exécution du script courant.Note : Le type de retour
neverindique une fonction qui ne retourne jamais, c.-à-d. qui appelleexit(), lève une exception, ou est une boucle infinie. -
Utilisons la redirection dans un premier scénario d’erreur. Dans
ControleurUtilisateur::afficherDetail, si leloginne correspond à aucun utilisateur, utilisez la redirection précédente vers l’URL de l’actionafficherListeavec le message flash de typedangersuivant :Login inconnu.Aide : Par exemple, pour rediriger vers l’action
bonjourdu contrôleurexempleavec la donnéevar=aavec un message flash de succèsOK, on peut écrire :ControleurUtilisateur::redirigerVersURL('controleurFrontal.php?controleur=exemple&action=bonjour&var=a&messagesFlash[success][]=OK'); -
Dans
ControleurUtilisateur::supprimer, si l’utilisateur a bien été supprimé, redirigez-le vers l’actionafficherListeavec le message flash de succèsL'utilisateur a bien été supprimé. - Vérifiez que tout fonctionne et que les messages flash s’affichent bien.
Application des sessions : messages flash
Le client est sur le formulaire de création d’une voiture :

Quand le formulaire est valide, le client est redirigé vers la vue qui liste toutes les voitures avec un message flash de succès :

Nous souhaitons maintenant que ce message ne s’affiche qu’une fois ; si le
client rafraîchit la page (F5), alors le message flash disparaît :

Un message flash est détruit dès qu’il est lu : il ne s’affiche donc qu’une seule fois.
Les messages flash seront stockés en session pour pouvoir être écrits lors d’une requête et lus lors de la requête de redirection suivante. Prenons un exemple :
-
En cliquant sur le bouton
Envoyerdu formulaire de création de voiture, le client fait une requête à l’actioncreerDepuisFormulaire. Si la création se passe bien, le contrôleur enregistre en session un message flash de succès, puis redirige le client vers l’affichage des voitures. -
Sur la page de l’action
afficherListe, le contrôleur va lire les messages flash stockés en session. Ces messages sont affichés sur la page. Selon le principe des messages flash, cette lecture entraînera leur suppression de la session. -
Si le client rafraîchit la page, l’action
afficherListeva toujours lire les messages flash, mais n’en trouvera pas. Le message disparaîtra donc.
Dans l’exercice suivant, vous allez manipuler les sessions (lors
du TD7, vous avez créé une classe Session pour faciliter la manipulation des
données). Vous allez notamment stocker et lire le tableau à deux dimensions contenant
les messages flash.
Attention, en PHP, les tableaux sont copiés par valeur : quand on récupère un tableau depuis les données de session, on obtient une copie. Si on modifie cette copie, les modifications ne sont pas répercutées dans le tableau stocké en session. Il faut réenregistrer le tableau après modification pour écraser l’ancien :
$tabA = [1,2,3,4];
Session::getInstance()->enregistrer('tableau', $tabA);
$tabB = Session::getInstance()->lire('tableau');
//$tabB => [1,2,3,4]
$tabB[] = 5;
//$tabB => [1,2,3,4,5]
$tabC = Session::getInstance()->lire('tableau');
//$tabC => [1,2,3,4] -> les modifications réalisées sur $tabB ne sont pas répercutées.
Session::getInstance()->enregistrer('tableau', $tabB);
$tabC = Session::getInstance()->lire('tableau');
//$tabC => [1,2,3,4,5]
-
Implémentez un système de message flash en complétant le squelette suivant dans le fichier
src/Lib/MessageFlash.php:namespace App\Covoiturage\Lib; use App\Covoiturage\Modele\HTTP\Session; class MessageFlash { // Les messages sont enregistrés en session associée à la clé suivante private static string $cleFlash = "_messagesFlash"; /** * Renvoie le tableau à deux dimensions ne contenant aucun message. * Ce tableau sert de valeur initiale lorsque aucun message flash n'est * stocké en session, et de valeur de réinitialisation après lecture. * @return string[][] */ private static function messagesVides(): array { return ["success" => [], "info" => [], "warning" => [], "danger" => []]; } /** * Renvoie le tableau à deux dimensions stocké en session à la clé * MessageFlash::$cleFlash. * * Si cette clé n'est pas présente en session, renvoie le tableau * sans message donné par MessageFlash::messagesVides(). * * Remarque : c'est le serveur qui écrit cette donnée en session. * Si elle est présente, on peut donc supposer qu'elle a le bon format. * @return string[][] */ private static function lireSession(): array { // À compléter return []; } /** * Récupère le tableau des messages flashs grâce à MessageFlash::lireSession(), * puis ajoute une entrée contenant $message à la fin du sous-tableau * associé à la clé $type. * Enregistre ensuite ce tableau en session à la clé MessageFlash::$cleFlash. */ public static function ajouter(string $type, string $message): void { // À compléter return; } /** * Renvoie le tableau à deux dimensions contenant tous les messages flashs, * rangés par type, obtenu grâce à MessageFlash::lireSession(). * * Attention : la lecture doit détruire tous les messages. Avant de renvoyer * le tableau, il faut donc enregistrer en session, le tableau sans message * donné par MessageFlash::messagesVides(). * @return string[][] */ public static function lireTousMessages(): array { // À compléter return []; } } - Modifiez
ControleurGenerique::afficherVue()pour lire les messages flash depuis cette classe avec :$messagesFlash = MessageFlash::lireTousMessages(); - Testez vos nouveaux messages flash. Pour cela, reprenez les scénarios de
la section Redirection. Au lieu de passer les messages flash dans l’URL, vous les
ajouterez dans la session avec
MessageFlash::ajouter()avant la redirection.
Amélioration du système de redirection
La méthode redirigerVersURL fonctionne, mais n’est pas forcément très pratique à utiliser :
- Il faut créer manuellement le query string.
- De plus, techniquement, il faudrait encoder les données avec
rawurlencode(cf. TD5) pour qu’un caractère comme&ou=dans une valeur ne casse pas le query string.
Nous allons mettre en place une méthode plus pratique qui fera tout cela automatiquement :
public static function rediriger(string $controleur = "", string $action = "", array $query = []): never
{
$tableauQueryString = [];
if($controleur !== "") {
$tableauQueryString[] = "controleur=$controleur";
}
if($action !== "") {
$tableauQueryString[] = "action=$action";
}
foreach ($query as $name => $value) {
$valueURL = rawurlencode($value);
$tableauQueryString[] = "$name=$valueURL";
}
$queryString = !empty($tableauQueryString) ? ("?" . join("&", $tableauQueryString)) : "";
$url = "controleurFrontal.php" . $queryString;
ControleurGenerique::redirigerVersURL($url);
}
Grâce à cette nouvelle méthode, il sera possible de rediriger plus facilement l’utilisateur.
Par exemple, si on souhaite rediriger vers l’action monAction du contrôleur exemple :
//Redirige vers controleurFrontal.php?controleur=exemple&action=monAction
ControleurGenerique::rediriger('exemple', 'monAction');
Le tableau $query est un tableau associatif qui permet de venir compléter le query string avec des
données utiles à l’action ciblée (un peu comme ce qu’on fait déjà avec afficherVue et les données
transmises par le contrôleur aux vues).
Par exemple, si on souhaite fournir des données à l’action de l’exemple précédent :
//Redirige vers controleurFrontal.php?controleur=exemple&action=monAction&varA=valeurA&varB=5
ControleurGenerique::rediriger('exemple', 'monAction', ["varA" => "valeurA", "varB" => 5]);
-
Ajoutez la méthode
redirigerau contrôleur générique, en copiant le code ci-dessus. -
Prenez bien le temps de comprendre ce que fait cette méthode, ligne par ligne. Demandez à votre enseignant chargé de TD si vous n’êtes pas sûr de comprendre certaines parties.
-
Remplacez les appels à
redirigerVersURLparredirigerdans les actionsControleurUtilisateur::afficherDetailetControleurUtilisateur::supprimer. -
Testez et vérifiez que tout fonctionne et que vos messages flash s’affichent toujours.
Utilisation des messages flash
Utilisez les messages flash pour enlever toutes les vues qui affichaient un message puis incluaient une autre vue.
-
En particulier, supprimez les vues désormais inutiles comme
utilisateurCree.php,utilisateurConnecte.php,utilisateurDeconnecte.php,utilisateurMisAJour.phpetutilisateurSupprime.php. Faites de même pour les vues trajets et éventuellement les vues concernant les passagers si vous aviez fait certaines questions bonus. -
Remplacez ces vues par des messages flash de type
successdans les actions concernées.-
Par exemple, en cas de succès de création d’un utilisateur, on pourrait ajouter un message flash de succès L’utilisateur a bien été créé puis rediriger vers l’action
afficherListedu contrôleurutilisateur. -
Ou bien, pour la connexion, on pourra ajouter un message flash de succès Connexion réussie puis rediriger vers l’action
afficherDetaildu contrôleurutilisateuren passant le login de l’utilisateur dans l’URL grâce au tableau associatif$queryde la méthoderediriger.
-
-
L’action
enregistrerPreference()doit maintenant rediriger vers l’action par défaut du contrôleur par défaut, que l’on obtient sans indiquer d’action ni de contrôleur dans la méthoderediriger, avec un message flash de succès La préférence de contrôleur est enregistrée.
Dans votre site, nous allons remplacer tous les appels à afficherErreur (qui
affichent la vue d’erreur) par des redirections avec messages flash.
Nous réserverons les messages flash de type danger pour les
erreurs qui ne sont censées se produire que si le client a essayé de hacker
le site (accès à une page qu’on ne lui a pas proposée, donnée de formulaire
manquante alors qu’elle était required), et les messages flash de type
warning pour les erreurs de l’utilisateur.
-
Action
afficherDetail- s’il manque le
login, redirigez vers la liste des utilisateurs avec un message flash Login manquant de type danger.
- s’il manque le
-
Action
creerDepuisFormulaire-
s’il manque des données, redirigez vers le formulaire de création avec un message flash Données manquantes de type danger.
-
si les deux champs mot de passe ne coïncident pas, redirigez vers le formulaire de création avec un message flash Mots de passe distincts de type warning.
-
si le format de l’email n’est pas valide, redirigez vers le formulaire de création avec un message flash Adresse email invalide de type warning.
-
si la sauvegarde échoue (par exemple parce que le login est déjà pris), redirigez vers le formulaire de création avec un message flash Échec de la sauvegarde de type warning.
-
- Action
afficherFormulaireMiseAJour:-
s’il manque le
login, redirigez vers la liste des utilisateurs avec un message flash Login manquant de type danger. -
si l’utilisateur qui modifie le profil n’est pas l’utilisateur à qui appartient le profil ou un administrateur, redirigez vers la liste des utilisateurs avec un message flash La mise à jour n’est possible que pour l’utilisateur connecté de type danger.
-
si l’utilisateur n’existe pas, redirigez vers la liste des utilisateurs avec un message flash Utilisateur inexistant de type danger.
-
-
Action
mettreAJour:-
s’il manque des données, redirigez vers la liste des utilisateurs avec un message flash Données manquantes de type danger.
-
si l’utilisateur n’existe pas, redirigez vers la liste des utilisateurs avec un message flash Utilisateur inexistant de type danger.
-
si l’utilisateur qui modifie le profil n’est pas l’utilisateur à qui appartient le profil ou un administrateur, redirigez vers la liste des utilisateurs avec un message flash La mise à jour n’est possible que pour l’utilisateur connecté de type danger.
-
si les 2 nouveaux mots de passe ne coïncident pas, redirigez vers le formulaire de mise à jour en indiquant le login dans l’URL (toujours grâce au paramètre
$queryderediriger) avec un message flash Mots de passe distincts de type warning. -
si l’ancien mot de passe n’est pas correct et que l’utilisateur n’est pas administrateur, redirigez vers le formulaire de mise à jour en indiquant le login dans l’URL avec un message flash Ancien mot de passe erroné de type warning.
-
si le format du nouvel email n’est pas valide, redirigez vers le formulaire de mise à jour en indiquant le login dans l’URL avec un message flash Adresse email invalide de type warning.
-
- Action
supprimer:-
s’il manque le
login, redirigez vers la liste des utilisateurs avec un message flash Login manquant de type danger. -
si l’utilisateur qui supprime l’utilisateur n’est pas l’utilisateur à qui appartient le profil ou un administrateur, redirigez vers la liste des utilisateurs avec un message flash La suppression n’est possible que pour l’utilisateur connecté de type danger.
-
si la suppression a échoué, par exemple parce que l’utilisateur n’existe pas, redirigez vers la liste des utilisateurs avec un message flash Utilisateur inexistant de type danger.
-
- Action
connecter:-
si le login ou le mot de passe ne sont pas transmis, redirigez vers le formulaire de connexion avec un message flash Login et/ou mot de passe manquant de type danger.
-
si l’utilisateur n’existe pas ou que le mot de passe est incorrect, redirigez vers le formulaire de connexion avec un message flash Login et/ou mot de passe incorrect de type warning.
-
si l’utilisateur n’a pas validé son adresse email, redirigez vers le formulaire de connexion avec un message flash Votre adresse email n’est pas validée de type warning.
-
-
Action
validerEmail:-
s’il manque le
loginet/ou lenonce, redirigez vers la liste des utilisateurs avec un message flash Données manquantes de type danger. -
en cas de succès de la validation de l’email, redirigez vers la page de détail de cet utilisateur (en passant son login dans l’URL grâce au paramètre
$queryderediriger) avec un message flash Adresse email validée de type success. -
en cas d’échec de la validation de l’email, redirigez vers la liste des utilisateurs avec un message flash Erreur lors de la validation de type danger.
-
- Remplacez les autres appels à
afficherErreurrestants (s’ils n’ont pas été listés dans cet exercice), notamment ceux des autres contrôleurs. Par exemple :- à la création d’un trajet, si le login du conducteur ne correspond à aucun utilisateur, redirigez vers le formulaire de création avec un message flash Conducteur inconnu de type warning ;
- si vous avez codé le contrôleur passager (bonus du TD6), vérifiez la présence des paramètres et l’existence du trajet, et traitez l’échec de l’inscription (login inconnu, passager déjà inscrit).
-
Normalement, à ce stade, vous ne devriez plus avoir d’appel à
afficherErreurdans vos contrôleurs. Nous allons donc, a priori, pouvoir supprimer cette méthode. Cependant, elle est encore utilisée à un dernier endroit : le point d’entrée de notre site,controleurFrontal.php. Nous pouvons mettre en place deux solutions :-
Soit remplacer les appels à
afficherErreurdanscontroleurFrontal.phppar un ajout de messages flash de typedangeret une redirection vers le contrôleur et l’action par défaut (en appelantControleurGenerique::redirigersans paramètres).
Attention : ne redirigez pas vers le contrôleur demandé (rediriger($controleur)). Par exemple, pourcontroleurFrontal.php?controleur=preference, l’action par défautafficherListen’existe pas dans ce contrôleur : la redirection tournerait en boucle. -
Soit créer une vue globale
erreur.phpdans le dossiersrc/vuepuis une méthode statiqueafficherErreur($message)dansControleurGeneriquequi appellera cette vue en lui passant le message d’erreur (proche de ce que nous avions jusqu’ici). Il suffit alors d’utiliserControleurGenerique::afficherErreurdanscontroleurFrontal.phpà la place de ce qui était utilisé jusqu’ici.
Choisissez une des deux solutions et implémentez-la. Ensuite, vérifiez dans chaque contrôleur (sauf le contrôleur générique) que la méthode
afficherErreurn’est plus utilisée et supprimez-la. -
- Tentez d’accéder à un contrôleur et/ou une action qui n’existe pas et vérifiez que le message d’erreur est affiché correctement.
Testez aussi
controleurFrontal.php?controleur=preference(sans action).