Afficher les erreurs et conserver la saisie
Objectif : Réafficher le formulaire d’ajout avec les valeurs saisies et des erreurs compréhensibles après un échec de validation.
Un formulaire qui rejette une saisie sans indiquer le champ concerné oblige la personne à deviner quoi corriger. Et si la page efface aussi les valeurs déjà saisies, elle impose de recommencer. Vous allez préparer les erreurs et les valeurs avant le rendu, puis réafficher le formulaire pour permettre une correction ciblée.
Préparer les erreurs et conserver la saisie
À la leçon précédente, vous avez vérifié le nom et le courriel côté serveur. La nouveauté est de ne plus réduire le résultat à un message générique : vous rangez les explications dans $erreurs, avec une entrée pour chaque champ. Vous conservez parallèlement les valeurs dans $valeurs, afin de les transmettre au gabarit même lorsque la validation échoue.
Le fichier ci-dessous remplace la version précédente de contact-ajouter.php. Les contrôles s’exécutent seulement après une soumission POST ; une ouverture directe présente un formulaire vierge. Les erreurs sont préparées avant les inclusions de rendu, conformément à la séparation entre traitement et affichage déjà utilisée dans le projet.
<?php
declare(strict_types=1);
$estSoumission = ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST';
$valeurs = [
'nom' => '',
'courriel' => '',
];
$erreurs = [];
$message = '';
if ($estSoumission) {
$nomSoumis = filter_input(INPUT_POST, 'nom');
$courrielSoumis = filter_input(INPUT_POST, 'courriel');
$valeurs['nom'] = is_string($nomSoumis) ? trim($nomSoumis) : '';
$valeurs['courriel'] = is_string($courrielSoumis)
? trim($courrielSoumis)
: '';
if ($valeurs['nom'] === '') {
$erreurs['nom'] = 'Saisissez un nom.';
} elseif (strlen($valeurs['nom']) > 100) {
$erreurs['nom'] = 'Le nom ne doit pas dépasser 100 octets.';
}
if ($valeurs['courriel'] === '') {
$erreurs['courriel'] = 'Saisissez une adresse e-mail.';
} elseif (filter_var($valeurs['courriel'], FILTER_VALIDATE_EMAIL) === false) {
$erreurs['courriel'] = 'Saisissez une adresse e-mail valide.';
}
if ($erreurs === []) {
$message = 'Les champs sont valides. Le contact ne sera pas encore enregistré.';
}
}
require __DIR__ . '/includes/entete.php';
?>
<section aria-labelledby="titre-contact">
<h2 id="titre-contact">Ajouter un contact</h2>
<?php if ($message !== ''): ?>
<p class="carnet-message" role="status"><?= htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
<?php elseif ($erreurs !== []): ?>
<p class="carnet-message carnet-message--erreur" role="alert">
Vérifiez les champs signalés ci-dessous.
</p>
<?php endif; ?>
<form class="carnet-formulaire" action="contact-ajouter.php" method="post">
<label class="carnet-champ" for="nom">Nom</label>
<input id="nom" name="nom" type="text" required maxlength="100"
value="<?= htmlspecialchars($valeurs['nom'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"<?= isset($erreurs['nom']) ? ' aria-invalid="true" aria-describedby="erreur-nom"' : '' ?>>
<?php if (isset($erreurs['nom'])): ?>
<p class="carnet-message carnet-message--erreur" id="erreur-nom" role="alert"><?= htmlspecialchars($erreurs['nom'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
<?php endif; ?>
<label class="carnet-champ" for="courriel">Adresse e-mail</label>
<input id="courriel" name="courriel" type="email" required
value="<?= htmlspecialchars($valeurs['courriel'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"<?= isset($erreurs['courriel']) ? ' aria-invalid="true" aria-describedby="erreur-courriel"' : '' ?>>
<?php if (isset($erreurs['courriel'])): ?>
<p class="carnet-message carnet-message--erreur" id="erreur-courriel" role="alert"><?= htmlspecialchars($erreurs['courriel'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
<?php endif; ?>
<label class="carnet-champ" for="telephone">Téléphone</label>
<input id="telephone" name="telephone" type="tel">
<div class="carnet-actions">
<button type="submit">Enregistrer</button>
<button type="reset">Effacer</button>
</div>
</form>
</section>
<?php require __DIR__ . '/includes/pied.php'; ?>Les erreurs sont indexées avec les mêmes noms que les champs : nom ou courriel. Ainsi, le gabarit affiche une explication sous le champ correspondant, plutôt qu’une seule indication ambiguë. Le téléphone reste présent dans le contrat du formulaire, mais il n’est pas encore validé ni conservé : cette leçon porte uniquement sur les deux champs déjà contrôlés.
Relier les erreurs aux champs
Quand la validation échoue, l’attribut aria-invalid="true" indique que le champ est en erreur. aria-describedby relie le champ à l’identifiant du paragraphe qui explique comment le corriger. Ces attributs ne remplacent pas le texte visible : ils ajoutent une relation compréhensible par les technologies d’assistance.
Observer le résultat
Essayez une adresse invalide en désactivant temporairement la validation du navigateur, ou envoyez directement une requête de test. La page conserve les champs nom et courriel, indique l’erreur sous le champ concerné et présente un résumé général. Avec des données conformes, elle affiche un état de validation ; aucun contact n’est encore ajouté au carnet.
Les valeurs réaffichées passent par htmlspecialchars, comme toute donnée dynamique insérée dans le HTML. Une chaîne saisie ne doit jamais être considérée comme du balisage sûr. Le formulaire réutilise les inclusions communes includes/entete.php et includes/pied.php ; la section affichée reste à l’intérieur du contenu principal prévu par ces gabarits.
À retenir
- Rangez une erreur par champ dans un tableau indexé par son nom.
- Conservez les valeurs reçues pour les réafficher après un échec.
- Échappez les valeurs avant de les insérer dans le HTML.
- Associez chaque champ invalide à son explication avec aria-describedby et signalez son état avec aria-invalid.
- Utilisez required pour aider la saisie dans le navigateur, sans supprimer la validation PHP.
Dans la prochaine leçon, vous isolerez la lecture et l’écriture des contacts dans un fichier JSON, sans modifier le contrat du formulaire.