COURS COMPLET
PHP & MySQL avec Bootstrap
Tables liées, Authentification, AJAX et JavaScript dynamique
Projet : RGPH5 BÉNIN
Introduction et Concepts Clés
Les nouveaux concepts de ce cours
Concept Description
Tables liées Deux tables reliées entre elles via une clé
étrangère
Clé étrangère Une colonne qui pointe vers l'id d'une autre
table
Authentification Système de connexion avec email et mot de
passe
Session PHP Mécanisme qui garde l'utilisateur connecté
entre les pages
Bootstrap Framework CSS qui donne un style
professionnel sans écrire de CSS
AJAX Technique JavaScript pour communiquer avec
PHP sans recharger la page
JOIN SQL Requête qui combine des données de plusieurs
tables
SUM SQL Fonction SQL qui calcule la somme d'une
colonne
Structure de la base de données RGPH5
Le schéma de la base de données suit une hiérarchie : un département contient plusieurs
communes, et une commune contient plusieurs villes.
Table Colonnes Lien
departement iddepartement, nom Table principale
commune idcommune, iddepartement, Liée à departement via
nom iddepartement
ville idville, idcommune, nom, Liée à commune via
population idcommune
utilisateur idutilisateur, email, mdp Table indépendante
🔑 Règle d'or : Toujours créer les tables dans l'ordre : d'abord les tables sans clé
étrangère, ensuite celles qui en ont. Ici : departement → commune → ville →
utilisateur.
Bootstrap — Les classes essentielles
Classe Bootstrap Effet
container Centre le contenu avec des marges
automatiques
mt-4, mb-3 Marge en haut (mt) ou en bas (mb). Le chiffre =
taille (1 à 5)
btn btn-primary Bouton bleu
btn btn-danger Bouton rouge
btn btn-success Bouton vert
btn btn-secondary Bouton gris
btn btn-warning Bouton orange
table table-bordered Tableau avec bordures
table-striped Tableau avec lignes alternées
table-dark Ligne de tableau foncée (pour le total)
form-control Style pour les inputs et selects
form-label Style pour les labels
alert alert-success Message de succès vert
alert alert-danger Message d'erreur rouge
alert alert-warning Message d'avertissement orange
card Carte avec bordure et ombre
card-header En-tête de carte
card-body Corps de carte
1. [Link] — Universel
Ce fichier est identique à tous les projets. Seul le nom de la base de données change.
<?php
$host = "localhost";
$dbname = "NOM_BASE_DE_DONNEES";
$user = "root";
$password = "";
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Erreur de connexion : " . $e->getMessage());
}
?>
🔑 Règle d'or : [Link] ne contient jamais de HTML. C'est un fichier
purement technique.
2. Authentification — [Link]
C'est quoi une session ?
Une session PHP permet de garder des informations entre plusieurs pages. Quand
l'utilisateur se connecte, on crée une session avec son badge. Chaque page protégée vérifie
ce badge.
Code Explication
session_start() Démarre le système de session. Doit être la
1ère ligne de chaque page protégée.
$_SESSION['connecte'] = true Crée le badge de connexion
isset($_SESSION['connecte']) Vérifie si le badge existe
session_destroy() Supprime la session (déconnexion)
[Link] — Code universel
<?php
session_start();
include '[Link]';
$erreur = "";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$email = $_POST['email'];
$mdp = $_POST['mdp'];
$sql = "SELECT * FROM NOM_TABLE_UTILISATEUR WHERE email = :email AND mdp
= :mdp";
$stmt = $pdo->prepare($sql);
$stmt->execute([':email' => $email, ':mdp' => $mdp]);
$utilisateur = $stmt->fetch();
if ($utilisateur) {
$_SESSION['connecte'] = true;
header('Location: [Link]');
exit;
} else {
$erreur = "Email ou mot de passe incorrect !";
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>NOM_PROJET - Connexion</title>
<link rel="stylesheet" href="css/[Link]">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-4">
<h2 class="text-center mb-4">NOM_PROJET</h2>
<?php if ($erreur != '') : ?>
<div class="alert alert-danger"><?php echo $erreur; ?></div>
<?php endif; ?>
<form action="[Link]" method="POST">
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control"
required>
</div>
<div class="mb-3">
<label class="form-label">Mot de passe</label>
<input type="password" name="mdp" class="form-control"
required>
</div>
<button type="submit" class="btn btn-primary w-100">Se
connecter</button>
</form>
</div>
</div>
</div>
<script src="js/[Link]"></script>
</body>
</html>
Ce que tu changes selon l'énoncé :
Majuscule Ce que tu mets
NOM_PROJET RGPH5 BÉNIN, Gestion École...
NOM_TABLE_UTILISATEUR utilisateur, users, admin...
[Link] — Universel
<?php
session_start();
session_destroy();
header('Location: [Link]');
exit;
?>
🔑 Règle d'or : Ce fichier est identique dans tous les projets avec authentification.
Ne jamais le modifier.
Protection des pages — Code universel
Chaque page protégée doit commencer par ces 4 lignes :
<?php
session_start();
if (!isset($_SESSION['connecte'])) {
header('Location: [Link]');
exit;
}
?>
🔑 Règle d'or : Si l'utilisateur n'est pas connecté, il est redirigé vers [Link]. Ces
4 lignes doivent être au tout début de chaque page protégée, avant tout HTML.
3. AJAX et Select Dynamique
C'est quoi AJAX ?
AJAX permet à JavaScript d'envoyer une requête à un fichier PHP sans recharger la page.
C'est ce qu'on utilise pour charger les communes quand on change le département.
Étape Ce qui se passe
1 L'utilisateur change le département dans le
select
2 JavaScript détecte le changement avec
addEventListener
3 JavaScript envoie l'id du département à
get_communes.php via fetch()
4 get_communes.php retourne les options HTML
des communes
5 JavaScript injecte ces options dans le select
Commune
6 Tout ça sans recharger la page !
get_communes.php — Code universel
Ce fichier reçoit l'id d'un parent et retourne les enfants correspondants sous forme d'options
HTML.
<?php
include '[Link]';
$id_parent = $_GET['ID_PARENT'];
"$sql = "SELECT * FROM TABLE_ENFANT WHERE ID_PARENT = :id_parent";
$stmt = $pdo->prepare($sql);
$stmt->execute([':id_parent' => $id_parent]);
$elements = $stmt->fetchAll();
foreach($elements as $element) {
echo '<option value="' . $element['ID_ENFANT'] . '">' . $element['nom'] .
'</option>';
}
?>
Exemple RGPH5 :
Majuscule Valeur RGPH5
ID_PARENT iddepartement
TABLE_ENFANT commune
ID_ENFANT idcommune
JavaScript AJAX — Code universel
Ce code va dans le fichier PHP qui contient le formulaire, juste avant </body> :
<script>
[Link]('ID_SELECT_PARENT').addEventListener('change',
function() {
var id_parent = [Link];
var selectEnfant = [Link]('ID_SELECT_ENFANT');
if (id_parent == '') {
[Link] = "<option value=''>-- Choisir d'abord
--</option>";
return;
}
fetch('NOM_FICHIER_PHP.php?ID_PARENT=' + id_parent)
.then(function(response) {
return [Link]();
})
.then(function(data) {
[Link] = "<option value=''>-- Choisir --</option>"
+ data;
});
});
</script>
Exemple RGPH5 :
Majuscule Valeur RGPH5
ID_SELECT_PARENT iddepartement (id du select département)
ID_SELECT_ENFANT idcommune (id du select commune)
NOM_FICHIER_PHP get_communes
ID_PARENT iddepartement
JavaScript sans bouton submit — Code universel
Pour afficher un résultat automatiquement sans bouton, on utilise le même principe :
<script>
[Link]('ID_SELECT').addEventListener('change', function() {
var id = [Link];
var resultat = [Link]('ID_DIV_RESULTAT');
if (id == '') {
[Link] = '';
return;
}
fetch('NOM_FICHIER_PHP.php?id=' + id)
.then(function(response) {
return [Link]();
})
.then(function(data) {
[Link] = data;
});
});
</script>
🔑 Règle d'or : Pour le JavaScript sans bouton submit, il faut un div avec un id
dans le HTML pour recevoir le résultat. Ex: <div id='resultat'></div>
4. Les Jointures SQL (JOIN)
C'est quoi un JOIN ?
Un JOIN permet de combiner des données de plusieurs tables dans une seule requête.
C'est indispensable quand on a des tables liées.
Type de JOIN Explication
JOIN ou INNER JOIN Retourne uniquement les lignes qui ont une
correspondance dans les deux tables
LEFT JOIN Retourne toutes les lignes de la table gauche,
même si pas de correspondance à droite
Exemples de JOIN — RGPH5
Récupérer les villes avec leur commune et département :
"$sql = "SELECT [Link] as nom_ville, [Link],
[Link] as nom_commune,
[Link] as nom_departement
FROM ville v
JOIN commune c ON [Link] = [Link]
JOIN departement d ON [Link] = [Link]";
Population totale par commune d'un département :
"$sql = "SELECT [Link], SUM([Link]) as total_population
FROM commune c
LEFT JOIN ville v ON [Link] = [Link]
WHERE [Link] = :iddepartement
GROUP BY [Link], [Link]";
Population totale par département :
"$sql = "SELECT [Link], SUM([Link]) as total_population
FROM departement d
LEFT JOIN commune c ON [Link] = [Link]
LEFT JOIN ville v ON [Link] = [Link]
GROUP BY [Link], [Link]";
Mot clé SQL Explication
JOIN ... ON Relie deux tables. ON précise la condition de
liaison
SUM(colonne) Calcule la somme des valeurs d'une colonne
GROUP BY Regroupe les résultats par colonne. Obligatoire
avec SUM
AS Donne un alias (surnom) à une colonne dans le
résultat
v., c., d. Alias de tables pour éviter les confusions entre
colonnes de même nom
5. Tous les fichiers du projet RGPH5
[Link]
<?php
session_start();
if (!isset($_SESSION['connecte'])) { header('Location: [Link]'); exit; }
include '[Link]';
$sql = "SELECT * FROM departement";
$stmt = $pdo->query($sql);
$departements = $stmt->fetchAll();
$message = "";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$idcommune = $_POST['idcommune'];
$nom = $_POST['nom'];
$population = $_POST['population'];
$sql = "INSERT INTO ville (idcommune, nom, population) VALUES
(:idcommune, :nom, :population)";
$stmt = $pdo->prepare($sql);
$stmt->execute([':idcommune' => $idcommune, ':nom' => $nom, ':population'
=> $population]);
$message = "Ville ajoutee avec succes !";
}
?>
<!DOCTYPE html><html lang='fr'><head><meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Ajouter population</title>
<link rel='stylesheet' href='css/[Link]'></head>
<body><div class='container mt-4'>
<h1>Ajouter la population d'une ville</h1>
<?php if ($message != '') : ?>
<div class='alert alert-success'><?php echo $message; ?></div>
<?php endif; ?>
<form action='[Link]' method='POST'>
<div class='mb-3'><label class='form-label'>Departement</label>
<select name='iddepartement' id='iddepartement' class='form-control'
required>
<option value=''>-- Choisir un departement --</option>
<?php foreach($departements as $dep) : ?>
<option value='<?php echo $dep["iddepartement"]; ?>'><?php echo
$dep["nom"]; ?></option>
<?php endforeach; ?>
</select></div>
<div class='mb-3'><label class='form-label'>Commune</label>
<select name='idcommune' id='idcommune' class='form-control' required>
<option value=''>-- Choisir d'abord un departement --</option>
</select></div>
<div class='mb-3'><label class='form-label'>Nom de la ville</label>
<input type='text' name='nom' class='form-control' required></div>
<div class='mb-3'><label class='form-label'>Population</label>
<input type='number' name='population' class='form-control' min='0'
required></div>
<button type='submit' class='btn btn-primary'>Enregistrer</button>
<a href='[Link]' class='btn btn-secondary'>Retour</a>
</form></div>
<script src='js/[Link]'></script>
<script>
[Link]('iddepartement').addEventListener('change', function()
{
var iddepartement = [Link];
var selectCommune = [Link]('idcommune');
if (iddepartement == '') {
[Link] = "<option value=''>-- Choisir d'abord un
departement --</option>";
return;
}
fetch('get_communes.php?iddepartement=' + iddepartement)
.then(r => [Link]())
.then(data => {
[Link] = "<option value=''>-- Choisir une commune
--</option>" + data;
});
});
</script></body></html>
get_communes.php
<?php
include '[Link]';
$iddepartement = $_GET['iddepartement'];
"$sql = "SELECT * FROM commune WHERE iddepartement = :iddepartement";
$stmt = $pdo->prepare($sql);
$stmt->execute([':iddepartement' => $iddepartement]);
$communes = $stmt->fetchAll();
foreach($communes as $commune) {
echo '<option value="' . $commune['idcommune'] . '">' . $commune['nom'] .
'</option>';
}
?>
[Link]
<?php
session_start();
if (!isset($_SESSION['connecte'])) { header('Location: [Link]'); exit; }
include '[Link]';
$sql = "SELECT * FROM commune";
$stmt = $pdo->query($sql); $communes = $stmt->fetchAll();
$villes = []; $total = 0; $commune_choisie = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$idcommune = $_POST['idcommune'];
$sql = "SELECT nom FROM commune WHERE idcommune = :idcommune";
$stmt = $pdo->prepare($sql); $stmt->execute([':idcommune' => $idcommune]);
$commune_choisie = $stmt->fetch()['nom'];
$sql = "SELECT * FROM ville WHERE idcommune = :idcommune";
$stmt = $pdo->prepare($sql); $stmt->execute([':idcommune' => $idcommune]);
$villes = $stmt->fetchAll();
$sql = "SELECT SUM(population) as total FROM ville WHERE idcommune
= :idcommune";
$stmt = $pdo->prepare($sql); $stmt->execute([':idcommune' => $idcommune]);
$total = $stmt->fetch()['total'];
}
?>
<!-- HTML avec Bootstrap -->
<form action='[Link]' method='POST'>
<select name='idcommune' class='form-control' required>
<option value=''>-- Choisir une commune --</option>
<?php foreach($communes as $commune) : ?>
<option value='<?php echo $commune["idcommune"]; ?>'><?php echo
$commune["nom"]; ?></option>
<?php endforeach; ?>
</select>
<button type='submit' class='btn btn-primary mt-2'>Afficher</button>
</form>
<?php if (!empty($villes)) : ?>
<table class='table table-bordered table-striped mt-3'>
<thead><tr><th>Ville</th><th>Population</th></tr></thead>
<tbody>
<?php foreach($villes as $ville) : ?>
<tr><td><?php echo $ville['nom']; ?></td>
<td><?php echo number_format($ville['population'], 0, ',', ' ');
?></td></tr>
<?php endforeach; ?>
</tbody>
<tfoot><tr class='table-dark'>
<td><strong>TOTAL</strong></td>
<td><strong><?php echo number_format($total, 0, ',', ' ');
?></strong></td>
</tr></tfoot>
</table>
<?php endif; ?>
get_ville.php
<?php
include '[Link]';
$idville = $_GET['idville'];
"$sql = "SELECT [Link] as nom_ville, [Link],
[Link] as nom_commune, [Link] as nom_departement
FROM ville v
JOIN commune c ON [Link] = [Link]
JOIN departement d ON [Link] = [Link]
WHERE [Link] = :idville";
$stmt = $pdo->prepare($sql);
$stmt->execute([':idville' => $idville]);
$ville = $stmt->fetch();
if ($ville) {
echo '<div class="card mt-3">';
echo '<div class="card-header bg-primary text-white"><h4>' .
$ville['nom_ville'] . '</h4></div>';
echo '<div class="card-body">';
echo '<p><strong>Commune :</strong> ' . $ville['nom_commune'] . '</p>';
echo '<p><strong>Departement :</strong> ' . $ville['nom_departement'] .
'</p>';
echo '<p><strong>Population :</strong> ' .
number_format($ville['population'], 0, ',', ' ') . ' habitants</p>';
echo '</div></div>';
}
?>
6. Récapitulatif Final
Tous les fichiers du projet RGPH5
Fichier Rôle Nouveauté
[Link] Connexion à la base de Identique aux autres projets
données
[Link] Page de connexion avec Session PHP, SELECT avec
authentification AND
[Link] Menu principal protégé Protection avec session
[Link] Déconnexion et suppression session_destroy()
de session
[Link] Ajouter une ville avec sa AJAX, select dynamique
population
get_communes.php Retourne les communes d'un Fichier AJAX PHP
département
[Link] Population des villes d'une SUM, POST avec tableau
commune
[Link] Population des communes JOIN, GROUP BY
d'un département
[Link] Population de tous les Double JOIN, SUM global
départements
[Link] Recherche d'une ville sans AJAX sans submit
bouton submit
get_ville.php Retourne les infos d'une ville Triple JOIN
Les erreurs fréquentes et solutions
Erreur Cause Solution
MySQL ne démarre pas Fichiers aria_log corrompus Supprimer aria_log.* dans
mysql/data/
MySQL bloqué par Avast Antivirus bloque xampp- Ajouter C:\xampp\ en
[Link] exception dans Avast
Incorrect database name Espace en trop dans $dbname Vérifier qu'il n'y a pas d'espace
'rgph5 ' dans $dbname
Select commune vide JavaScript ne trouve pas Vérifier que id='iddepartement'
l'élément et id='idcommune' sont bien
dans le HTML
Page non protégée Oubli de session_start() ou Ajouter les 4 lignes de
isset() protection au début de chaque
page
JOIN retourne vide Mauvaise condition ON Vérifier que les noms de
colonnes dans ON
correspondent aux vraies
colonnes
SUM retourne NULL Aucune donnée dans la table Utiliser $total ?? 0 pour
afficher 0 si NULL
Règles d'or à ne jamais oublier
1. session_start() doit être la toute première ligne de chaque page protégée, avant tout
HTML.
2. Les fichiers AJAX (get_communes.php, get_ville.php) ne contiennent jamais de HTML
complet.
3. L'id du select dans le HTML doit correspondre exactement à ce que JavaScript cherche
avec getElementById.
4. Avec GROUP BY, toutes les colonnes du SELECT doivent être soit dans GROUP BY, soit
dans une fonction comme SUM.
5. LEFT JOIN retourne toutes les lignes même sans correspondance. JOIN retourne
uniquement les lignes qui ont une correspondance.
6. number_format($valeur, 0, ',', ' ') formate les grands nombres avec des espaces. Ex:
150000 → 150 000.
7. Les fichiers AJAX doivent toujours inclure [Link] pour accéder à la base de
données.