Leçon 9 / 15

Écrire et rechercher des contacts

25 min PHP 8 — structurer une application (PDO, POO, sessions) Gratuit

Objectif : Implémenter l’ajout, la modification, la suppression et la recherche paramétrée de contacts dans le dépôt PDO.

Quand vous ajoutez un contact, vous devez transmettre ses données à MySQL sans construire une requête à partir de valeurs saisies. Vous devez aussi pouvoir modifier ou supprimer un enregistrement précis, puis retrouver des contacts à partir d’un terme de recherche.

Dans la leçon « Lire les contacts avec une requête préparée », vous avez déjà créé `lister()` et converti les lignes récupérées en objets `Contact`. Vous allez maintenant compléter le même dépôt : il reste responsable des opérations SQL, tandis que `Contact` conserve les règles propres à un contact.

Ajouter un contact et récupérer son identifiant

La requête `INSERT` nomme explicitement les colonnes à renseigner et réserve une marque `:nom` pour chaque valeur. Après exécution, `lastInsertId()` fournit l’identifiant attribué par MySQL ; la méthode le retourne sous forme d’entier. Le modèle `Contact` utilisé ici expose les méthodes `getNom()`, `getCourriel()` et `getTelephone()`, conformément à son rôle d’objet métier.

src/Depot/ContactDepot.phpPHP
<?php

declare(strict_types=1);

namespace Carnet\Depot;

use Carnet\Modele\Contact;
use PDO;

final class ContactDepot
{
    public function __construct(private PDO $connexion)
    {
    }

    /**
     * @return list<Contact>
     */
    public function lister(): array
    {
        $requete = $this->connexion->prepare(
            'SELECT id, nom, courriel, telephone FROM contacts ORDER BY nom'
        );
        $requete->execute();
        $lignes = $requete->fetchAll();

        return array_map(
            static fn (array $ligne): Contact => new Contact(
                (int) $ligne['id'],
                $ligne['nom'],
                $ligne['courriel'],
                $ligne['telephone']
            ),
            $lignes
        );
    }

    public function ajouter(Contact $contact): int
    {
        $requete = $this->connexion->prepare(
            'INSERT INTO contacts (nom, courriel, telephone)
             VALUES (:nom, :courriel, :telephone)'
        );
        $requete->execute([
            'nom' => $contact->getNom(),
            'courriel' => $contact->getCourriel(),
            'telephone' => $contact->getTelephone(),
        ]);

        return (int) $this->connexion->lastInsertId();
    }

    public function modifier(Contact $contact): bool
    {
        if ($contact->getId() === null) {
            return false;
        }

        $requete = $this->connexion->prepare(
            'UPDATE contacts
             SET nom = :nom, courriel = :courriel, telephone = :telephone
             WHERE id = :id'
        );
        $requete->execute([
            'nom' => $contact->getNom(),
            'courriel' => $contact->getCourriel(),
            'telephone' => $contact->getTelephone(),
            'id' => $contact->getId(),
        ]);

        return $requete->rowCount() > 0;
    }

    public function supprimer(int $id): bool
    {
        if ($id < 1) {
            return false;
        }

        $requete = $this->connexion->prepare(
            'DELETE FROM contacts WHERE id = :id'
        );
        $requete->execute(['id' => $id]);

        return $requete->rowCount() > 0;
    }

    /**
     * @return list<Contact>
     */
    public function rechercher(string $terme): array
    {
        $terme = str_replace(['!', '%', '_'], ['!!', '!%', '!_'], trim($terme));
        $motif = '%' . $terme . '%';
        $requete = $this->connexion->prepare(
            "SELECT id, nom, courriel, telephone
             FROM contacts
             WHERE nom LIKE :motif ESCAPE '!'
                OR courriel LIKE :motif ESCAPE '!'
                OR telephone LIKE :motif ESCAPE '!'
             ORDER BY nom"
        );
        $requete->execute(['motif' => $motif]);
        $lignes = $requete->fetchAll();

        return array_map(
            static fn (array $ligne): Contact => new Contact(
                (int) $ligne['id'],
                $ligne['nom'],
                $ligne['courriel'],
                $ligne['telephone']
            ),
            $lignes
        );
    }
}

Le fichier reprend `lister()` et ajoute quatre opérations, sans émettre de contenu. Les paramètres de `execute()` associent chaque marque à une valeur séparée de la chaîne SQL ; une donnée contenant une apostrophe, par exemple, reste une donnée et ne devient pas une instruction SQL. Le code suppose que la table `contacts` utilise les colonnes `id`, `nom`, `courriel` et `telephone` définies pour le projet.

Modifier et supprimer avec un identifiant

La méthode `modifier()` utilise `WHERE id = :id` pour limiter l’opération à un contact. Un objet sans identifiant ne représente pas un enregistrement existant : la méthode retourne donc `false` sans exécuter de requête. `supprimer()` refuse également les identifiants inférieurs à 1, puis retourne le résultat de `rowCount()` pour indiquer si une ligne a été supprimée.

Rechercher sans laisser les caractères spéciaux agir

La recherche compare un motif avec les trois champs textuels. Les caractères `%` et `_` ont une signification spéciale dans `LIKE` ; le code les protège avec le caractère d’échappement `!`, puis ajoute ses propres `%` pour rechercher le terme au milieu d’une valeur. Le paramètre reste lié à la requête, et les résultats sont convertis en objets `Contact` comme dans `lister()`.

Avec le terme `marie`, le motif transmis vaut `%marie%` : un contact dont le nom est `Marie Martin` peut correspondre, tout comme une adresse contenant cette suite de caractères. Un terme vide produit le motif `%%`, qui correspond à toutes les valeurs textuelles non nulles ; le contrôleur pourra décider de traiter une recherche vide comme une simple liste.

À retenir

  • Préparez chaque requête d’écriture et transmettez les données séparément des instructions SQL.
  • Utilisez `lastInsertId()` pour récupérer l’identifiant généré après un ajout.
  • Ciblez une ligne précise avec son identifiant pour modifier ou supprimer un contact.
  • Interprétez `rowCount()` avec prudence, surtout pour une mise à jour qui ne change aucune valeur.
  • Pour une recherche `LIKE`, liez le motif et échappez les caractères génériques saisis.

La prochaine leçon portera sur le hachage et la vérification du mot de passe qui protège l’accès au carnet.

Envie d'aller plus loin ?

Les cours Enovatic sont gratuits et le resteront. Si vous cherchez un accompagnement encadré, nos formations professionnelles sont animées par les mêmes formateurs.