Un carnet de contacts complet, avec :
Ce projet valide les blocs 1 à 4 du cours.
carnet-contacts/
├── data/
│ └── contacts.json ← créé automatiquement
├── includes/
│ ├── header.php
│ ├── nav.php
│ └── footer.php
├── index.php ← redirige vers liste.php
├── liste.php ← affiche tous les contacts
├── ajouter.php ← formulaire d'ajout
├── supprimer.php ← traitement suppression (pas de vue)
└── fonctions.php ← fonctions partagées
data/contacts.json — créé automatiquement au premier ajout :
[
{
"id": "1713100800",
"nom": "Alice Dupont",
"email": "alice@example.be",
"telephone": "0474 12 34 56",
"groupe": "Amis",
"date_ajout": "14/04/2025 à 16:00"
}
]
<?php
// Chemin vers le fichier de données
define('FICHIER_CONTACTS', __DIR__ . '/data/contacts.json');
/**
* Lire tous les contacts depuis le fichier JSON
* Retourne un tableau vide si le fichier n'existe pas
*/
function lireContacts(): array {
if (!file_exists(FICHIER_CONTACTS)) {
return [];
}
$contenu = file_get_contents(FICHIER_CONTACTS);
return json_decode($contenu, true) ?? [];
}
/**
* Sauvegarder le tableau de contacts dans le fichier JSON
*/
function sauvegarderContacts(array $contacts): void {
file_put_contents(
FICHIER_CONTACTS,
json_encode($contacts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
);
}
/**
* Trouver un contact par son id
* Retourne le contact ou null si non trouvé
*/
function trouverContact(string $id): ?array {
foreach (lireContacts() as $contact) {
if ($contact['id'] === $id) {
return $contact;
}
}
return null;
}
define()crée une constante globale — accessible partout sans variable.
: array,: void,: ?array→ indications de type (PHP 7+), bonne pratique.
<?php
require 'fonctions.php';
$pageTitle = "Mes contacts";
require 'includes/header.php';
require 'includes/nav.php';
// Lecture des contacts
$contacts = lireContacts();
// Recherche par nom (filtre si ?q= est dans l'URL)
$recherche = trim($_GET['q'] ?? '');
if (!empty($recherche)) {
$contacts = array_filter($contacts, function($c) use ($recherche) {
return stripos($c['nom'], $recherche) !== false
|| stripos($c['email'], $recherche) !== false;
});
}
// Trier par nom
usort($contacts, fn($a, $b) => strcmp($a['nom'], $b['nom']));
?>
<main class="container mt-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>📋 Carnet de contacts
<span class="badge bg-secondary"><?= count($contacts) ?></span>
</h1>
<a href="ajouter.php" class="btn btn-success">+ Ajouter</a>
</div>
<!-- Barre de recherche -->
<form method="GET" action="" class="mb-4">
<div class="input-group">
<input type="text"
name="q"
class="form-control"
placeholder="Rechercher par nom ou email..."
value="<?= htmlspecialchars($recherche) ?>">
<button class="btn btn-outline-secondary" type="submit">🔍</button>
<?php if ($recherche) : ?>
<a href="liste.php" class="btn btn-outline-danger">✕</a>
<?php endif; ?>
</div>
</form>
<!-- Messages flash -->
<?php if (isset($_GET['added'])) : ?>
<div class="alert alert-success">✅ Contact ajouté avec succès !</div>
<?php endif; ?>
<?php if (isset($_GET['deleted'])) : ?>
<div class="alert alert-warning">🗑️ Contact supprimé.</div>
<?php endif; ?>
<!-- Liste des contacts -->
<?php if (empty($contacts)) : ?>
<div class="alert alert-info">
<?= $recherche ? "Aucun résultat pour "$recherche"." : "Aucun contact. <a href='ajouter.php'>Ajouter le premier !</a>" ?>
</div>
<?php else : ?>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3">
<?php foreach ($contacts as $c) : ?>
<div class="col">
<div class="card h-100 shadow-sm">
<div class="card-body">
<h5 class="card-title">
<?= htmlspecialchars($c['nom']) ?>
<?php if (!empty($c['groupe'])) : ?>
<span class="badge bg-info text-dark float-end">
<?= htmlspecialchars($c['groupe']) ?>
</span>
<?php endif; ?>
</h5>
<p class="card-text mb-1">
📧 <a href="mailto:<?= htmlspecialchars($c['email']) ?>">
<?= htmlspecialchars($c['email']) ?>
</a>
</p>
<?php if (!empty($c['telephone'])) : ?>
<p class="card-text mb-1">📞 <?= htmlspecialchars($c['telephone']) ?></p>
<?php endif; ?>
<small class="text-muted">Ajouté le <?= $c['date_ajout'] ?></small>
</div>
<div class="card-footer text-end">
<!-- Suppression avec confirmation JS -->
<a href="supprimer.php?id=<?= $c['id'] ?>"
class="btn btn-sm btn-outline-danger"
onclick="return confirm('Supprimer <?= htmlspecialchars($c['nom']) ?> ?')">
🗑️ Supprimer
</a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</main>
<?php require 'includes/footer.php'; ?>
<?php
require 'fonctions.php';
$erreurs = [];
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Récupération et nettoyage
$nom = trim($_POST['nom'] ?? '');
$email = trim($_POST['email'] ?? '');
$telephone = trim($_POST['telephone'] ?? '');
$groupe = trim($_POST['groupe'] ?? '');
// Validation
if (empty($nom)) $erreurs[] = "Le nom est obligatoire.";
if (empty($email)) $erreurs[] = "L'email est obligatoire.";
elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) $erreurs[] = "L'email est invalide.";
// Vérifier les doublons
$contacts = lireContacts();
foreach ($contacts as $c) {
if (strtolower($c['email']) === strtolower($email)) {
$erreurs[] = "Cet email est déjà dans le carnet.";
break;
}
}
// Sauvegarde si pas d'erreur
if (empty($erreurs)) {
$contacts[] = [
"id" => (string)time(),
"nom" => $nom,
"email" => $email,
"telephone" => $telephone,
"groupe" => $groupe,
"date_ajout" => date("d/m/Y à H:i"),
];
sauvegarderContacts($contacts);
header("Location: liste.php?added=1");
exit;
}
}
$pageTitle = "Ajouter un contact";
require 'includes/header.php';
require 'includes/nav.php';
?>
<main class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<h1>➕ Ajouter un contact</h1>
<?php if (!empty($erreurs)) : ?>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach ($erreurs as $e) : ?>
<li><?= htmlspecialchars($e) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="POST" action="" class="mt-3">
<div class="mb-3">
<label class="form-label">Nom complet *</label>
<input type="text" name="nom" class="form-control"
value="<?= htmlspecialchars($_POST['nom'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Email *</label>
<input type="email" name="email" class="form-control"
value="<?= htmlspecialchars($_POST['email'] ?? '') ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Téléphone</label>
<input type="tel" name="telephone" class="form-control"
value="<?= htmlspecialchars($_POST['telephone'] ?? '') ?>"
placeholder="0474 00 00 00">
</div>
<div class="mb-3">
<label class="form-label">Groupe</label>
<select name="groupe" class="form-select">
<option value="">— Aucun groupe —</option>
<?php foreach (["Famille", "Amis", "Travail", "Clients", "Autre"] as $g) : ?>
<option value="<?= $g ?>"
<?= ($_POST['groupe'] ?? '') === $g ? 'selected' : '' ?>>
<?= $g ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success">Enregistrer</button>
<a href="liste.php" class="btn btn-outline-secondary">Annuler</a>
</div>
</form>
</div>
</div>
</main>
<?php require 'includes/footer.php'; ?>
<?php
require 'fonctions.php';
// Récupérer l'id à supprimer
$id = $_GET['id'] ?? '';
if (!empty($id)) {
// Lire tous les contacts
$contacts = lireContacts();
// Filtrer : garder tous sauf celui avec cet id
$contacts = array_filter($contacts, function($c) use ($id) {
return $c['id'] !== $id;
});
// Renuméroter les clés du tableau (array_filter conserve les anciens index)
$contacts = array_values($contacts);
// Sauvegarder
sauvegarderContacts($contacts);
}
// Rediriger vers la liste dans tous les cas
header("Location: liste.php?deleted=1");
exit;
<?php
// La page d'accueil redirige vers la liste des contacts
header("Location: liste.php");
exit;
| Critère | Points |
|---|---|
| Structure modulaire (includes) fonctionnelle | /3 |
| Lecture du fichier JSON et affichage correct | /3 |
| Formulaire d'ajout avec validation | /4 |
| Sauvegarde dans contacts.json | /3 |
| Suppression fonctionnelle | /3 |
| Recherche par nom/email | /2 |
| Interface Bootstrap soignée | /2 |
| Total | /20 |
| Extension | Compétence |
|---|---|
Modifier un contact (page modifier.php?id=xxx) |
Formulaire pré-rempli |
| Exporter la liste en CSV | fputcsv() |
| Importer des contacts depuis un fichier CSV | fgetcsv() |
| Paginer la liste (10 contacts par page) | array_slice() |
| Trier par groupe, par date | usort() |
| Migrer vers MySQL + PDO | Bloc suivant du cours |