Écrire et rechercher des contacts
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.
<?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.