Aller au contenu

Construire la plateforme Decoupe

Cette série d’exercice vise à construire Decoupe, une plateforme qui répertorie des salons de coiffure. On avance par étapes : d’abord les pages du site générées côté serveur, pour terminer avec une API qui expose les mêmes données en JSON.

Chaque exercice réutilise le précédent, alors conservez votre code d’une étape à l’autre.

Le travail sera fait dans un dossier decoupe, à créer dans le dossier XAMPP htdocs.

Toutes les pages s’appuient sur le même fichier de données, fourni ci-dessous. Il retourne un tableau de douze salons.

Chaque salon possède un identifiant, un nom, une ville, une note, un indicateur de promotion, un horaire et une liste de services. Chaque service précise son nom, un court descriptif, un prix de base et sa clientèle (homme, femme ou mixte).

decoupe/data/salons.php
<?php
return [
[
'id' => 1, 'nom' => 'Studio Ciseaux', 'ville' => 'Montréal',
'note' => 4.7, 'promotion' => true,
'horaire' => 'Mardi au samedi, 9 h à 18 h',
'services' => [
['nom' => 'Coupe homme', 'description' => 'Coupe classique aux ciseaux', 'prix_base' => 25, 'clientele' => 'homme'],
['nom' => 'Coloration', 'description' => 'Couleur complète', 'prix_base' => 60, 'clientele' => 'femme'],
['nom' => 'Taille de barbe','description' => 'Contour et entretien', 'prix_base' => 18, 'clientele' => 'homme'],
],
],
[
'id' => 2, 'nom' => 'La Belle Mèche', 'ville' => 'Québec',
'note' => 4.9, 'promotion' => false,
'horaire' => 'Lundi au vendredi, 10 h à 19 h',
'services' => [
['nom' => 'Coupe femme', 'description' => 'Coupe et brushing', 'prix_base' => 45, 'clientele' => 'femme'],
['nom' => 'Mise en plis', 'description' => 'Coiffure pour occasion', 'prix_base' => 35, 'clientele' => 'femme'],
],
],
[
'id' => 3, 'nom' => 'Tif et Style', 'ville' => 'Montréal',
'note' => 4.3, 'promotion' => true,
'horaire' => 'Mardi au samedi, 9 h à 17 h',
'services' => [
['nom' => 'Coupe homme', 'description' => 'Coupe tendance', 'prix_base' => 22, 'clientele' => 'homme'],
['nom' => 'Rasage traditionnel','description' => 'Rasage à la serviette chaude', 'prix_base' => 30, 'clientele' => 'homme'],
],
],
[
'id' => 4, 'nom' => 'Coupe et Compagnie', 'ville' => 'Laval',
'note' => 4.1, 'promotion' => false,
'horaire' => 'Lundi au samedi, 9 h à 20 h',
'services' => [
['nom' => 'Coupe enfant', 'description' => 'Pour les 12 ans et moins', 'prix_base' => 18, 'clientele' => 'mixte'],
['nom' => 'Coupe femme', 'description' => 'Coupe simple', 'prix_base' => 40, 'clientele' => 'femme'],
],
],
[
'id' => 5, 'nom' => "L'Atelier du Cheveu", 'ville' => 'Longueuil',
'note' => 4.6, 'promotion' => true,
'horaire' => 'Mardi au vendredi, 9 h à 18 h',
'services' => [
['nom' => 'Balayage', 'description' => 'Éclaircissement progressif', 'prix_base' => 90, 'clientele' => 'femme'],
['nom' => 'Coupe femme', 'description' => 'Coupe et coiffage', 'prix_base' => 42, 'clientele' => 'femme'],
],
],
[
'id' => 6, 'nom' => 'Barbe et Lame', 'ville' => 'Gatineau',
'note' => 4.8, 'promotion' => false,
'horaire' => 'Lundi au vendredi, 8 h à 17 h',
'services' => [
['nom' => 'Rasage traditionnel','description' => 'Rasage complet au coupe-chou', 'prix_base' => 32, 'clientele' => 'homme'],
['nom' => 'Taille de barbe', 'description' => 'Mise en forme et huile', 'prix_base' => 20, 'clientele' => 'homme'],
],
],
[
'id' => 7, 'nom' => 'Chez Manon', 'ville' => 'Sherbrooke',
'note' => 4.2, 'promotion' => false,
'horaire' => 'Mardi au samedi, 9 h à 18 h',
'services' => [
['nom' => 'Coupe femme', 'description' => 'Coupe et brushing', 'prix_base' => 38, 'clientele' => 'femme'],
['nom' => 'Coloration', 'description' => 'Retouche de racines', 'prix_base' => 55, 'clientele' => 'femme'],
],
],
[
'id' => 8, 'nom' => 'Le Salon Nordique', 'ville' => 'Trois-Rivières',
'note' => 4.5, 'promotion' => true,
'horaire' => 'Mercredi au dimanche, 10 h à 18 h',
'services' => [
['nom' => 'Coupe homme', 'description' => 'Coupe et coiffage', 'prix_base' => 26, 'clientele' => 'homme'],
['nom' => 'Coupe enfant', 'description' => 'Pour les 12 ans et moins', 'prix_base' => 16, 'clientele' => 'mixte'],
],
],
[
'id' => 9, 'nom' => 'Coiffure Éclat', 'ville' => 'Montréal',
'note' => 4.4, 'promotion' => false,
'horaire' => 'Lundi au vendredi, 9 h à 19 h',
'services' => [
['nom' => 'Mise en plis', 'description' => 'Coiffure pour occasion', 'prix_base' => 34, 'clientele' => 'femme'],
['nom' => 'Balayage', 'description' => 'Reflets sur mesure', 'prix_base' => 85, 'clientele' => 'femme'],
],
],
[
'id' => 10, 'nom' => 'Tendance Studio', 'ville' => 'Laval',
'note' => 4.0, 'promotion' => true,
'horaire' => 'Mardi au samedi, 10 h à 18 h',
'services' => [
['nom' => 'Coupe femme', 'description' => 'Coupe moderne', 'prix_base' => 39, 'clientele' => 'femme'],
['nom' => 'Coupe homme', 'description' => 'Coupe et dégradé', 'prix_base' => 24, 'clientele' => 'homme'],
],
],
[
'id' => 11, 'nom' => 'La Griffe', 'ville' => 'Québec',
'note' => 4.7, 'promotion' => false,
'horaire' => 'Lundi au samedi, 9 h à 18 h',
'services' => [
['nom' => 'Coloration', 'description' => 'Couleur complète', 'prix_base' => 62, 'clientele' => 'femme'],
['nom' => 'Coupe femme', 'description' => 'Coupe et brushing', 'prix_base' => 44, 'clientele' => 'femme'],
],
],
[
'id' => 12, 'nom' => "Ciseaux d'Or", 'ville' => 'Montréal',
'note' => 4.9, 'promotion' => true,
'horaire' => 'Mardi au dimanche, 9 h à 20 h',
'services' => [
['nom' => 'Coupe homme', 'description' => 'Coupe et coiffage', 'prix_base' => 28, 'clientele' => 'homme'],
['nom' => 'Rasage traditionnel','description' => 'Rasage à chaud', 'prix_base' => 33, 'clientele' => 'homme'],
['nom' => 'Balayage', 'description' => 'Éclaircissement doux', 'prix_base' => 95, 'clientele' => 'femme'],
],
],
];

Toute donnée affichée dans une page doit être échappée avec htmlspecialchars() : un nom de salon, une ville, un service, ou toute valeur venue des données ou du client.

FonctionRôle
htmlspecialchars($texte)Échapper une valeur avant de l’insérer dans du HTML.
number_format($n, 2, ',', ' ')Afficher un prix avec deux décimales, une virgule et un espace pour les milliers.
array_filter($liste, $test)Ne garder que les éléments qui satisfont une condition.
array_values($liste)Réindexer une liste après un filtrage.
count($liste)Compter les éléments d’un tableau.
trim($texte)Retirer les espaces au début et à la fin d’une chaîne.
json_encode et json_decodeConvertir entre un tableau PHP et du texte JSON.

Créez la page salons.php. La page charge les données présentées plus haut, puis affiche chaque salon avec son nom, sa ville et sa note. Le nom est un lien vers la page de détail, salon.php?id=..., que vous construirez à l’exercice suivant. Échappez toute donnée affichée.

Vérification Indice

require renvoie le tableau des salons. Parcourez-le avec foreach. Pour le lien, insérez l’identifiant dans l’URL : salon.php?id=<?= $salon['id'] ?>.

Créez maintenant la page salon.php. La page lit l’identifiant passé en paramètre et affiche le salon correspondant : nom, ville, note, horaire, et la liste des services avec leur descriptif, leur prix et leur clientèle.

Écrivez d’abord une fonction qui retrouve un salon par son identifiant, puis affichez un formulaire de prise de rendez-vous (méthode POST) demandant le nom du client et une date. Si l’identifiant ne correspond à aucun salon, affichez un message et renvoyez un statut 404 (avec http_response_code).

Signature : trouverSalon(array $salons, int $id): ?array

Vérification Indice

La fonction reprend le patron de recherche : un foreach, un return dès qu’un id correspond, et return null à la fin. Convertissez $_GET['id'] en entier avec (int). Pour le prix, number_format donne un affichage soigné. Le formulaire renvoie vers la même page, donc son action conserve l’identifiant.

Créez la page d’entrée du site accueil.php. Elle met en avant seulement les salons en promotion.

Écrivez une fonction qui reçoit tous les salons et retourne uniquement ceux dont la promotion est active, puis affichez le résultat.

Signature : salonsEnPromotion(array $salons): array

Vérification Indice

Parcourez les salons et ajoutez au résultat ceux dont promotion vaut true.
La fonction array_filter fait la même chose en une expression, avec une fonction fléchée : array_filter($salons, fn($s) => $s['promotion']).

Exercice 4 – Optimiser avec des fragments réutilisables

Section intitulée « Exercice 4 – Optimiser avec des fragments réutilisables »

Les pages salons.php et accueil.php répètent le même affichage de salon, et chaque page répétera bientôt la même structure de document (l’en-tête et le pied).
Optimisez le site en extrayant des fragments réutilisables :

  1. un layout : fragments/entete.php, qui ouvre la page à partir d’un titre, et fragments/pied.php, qui la referme ;
  2. une carte de salon : fragments/carte-salon.php, qui affiche un salon à partir d’une variable $salon.

Réécrivez ensuite les pages salons.php et accueil.php pour qu’elles incluent ces fragments au lieu de répéter le HTML.

Vérification Indice

include insère un fragment à l’endroit voulu. Un fragment voit les variables définies avant son inclusion : entete.php utilise $titre, et carte-salon.php utilise $salon.
Dans la boucle, on définit $salon à chaque tour (le foreach s’en charge), puis on inclut la carte.

API

Créez la page api/salons.php. Ce point d’accès retourne la liste des salons en JSON, pour un programme qui souhaite les données sans la présentation. Annoncez le format avec l’en-tête approprié.

Attention au chemin : le fichier se trouve dans api/, alors que les données sont dans data/. Utilisez __DIR__ pour un chemin fiable.
Vérification Indice

header('Content-Type: application/json; charset=utf-8') annonce le format, puis json_encode produit le JSON.
Depuis api/salons.php, le fichier de données se trouve à __DIR__ . '/../data/salons.php'. L’option JSON_UNESCAPED_UNICODE garde les accents lisibles.

Exercice 6 Filtrer avec des paramètres de requête

Section intitulée « Exercice 6 Filtrer avec des paramètres de requête »

API et paramètres

Enrichissez api/salons.php pour qu’il accepte deux filtres dans la chaîne de requête : ?ville=Montréal ne retourne que les salons de cette ville, et ?promo=1 ne retourne que les salons en promotion. Les deux filtres peuvent se combiner.

Vérification Indice

Lisez les filtres dans $_GET. Appliquez array_filter sur la liste pour chaque filtre présent. Après un array_filter, les clés d’origine sont conservées, ce qui produirait un objet JSON plutôt qu’un tableau : array_values réindexe la liste avant l’encodage.

API et POST

Ajoutez la prise en charge de la méthode POST à api/salons.php. Une requête POST transporte un salon en JSON dans son corps. Lisez ce corps, vérifiez que le nom et la ville sont présents, puis répondez avec le statut 201 et le salon créé. Si les données sont incomplètes, répondez avec le statut 400. Une requête GET conserve le comportement des exercices précédents.

Vérification Indice

Consultez $_SERVER['REQUEST_METHOD'] pour aiguiller.
Un corps JSON se lit avec file_get_contents('php://input'), puis json_decode(..., true).
Vérifiez les champs avec isset avant de construire le salon. Attribuez un nouvel identifiant, par exemple count($salons) + 1.