Ajouter et vérifier la recherche
Objectif : Intégrer la recherche paramétrée du carnet, vérifier ses résultats et comparer ses performances avant et après optimisation.
Une recherche peut sembler correcte tout en renvoyant des contacts hors sujet, en oubliant des résultats ou en devenant lente à mesure que le carnet grandit. Vous allez relier le terme saisi au parcours existant, vérifier précisément les résultats et comparer les temps obtenus avant et après une optimisation.
Le dépôt utilise déjà des requêtes préparées, et le carnet affiche ses résultats par pages. Ici, vous ajoutez le terme de recherche à ce parcours sans modifier les règles de sécurité déjà vues : la valeur saisie reste un paramètre, jamais un fragment SQL.
Faire circuler le terme de recherche
Dans le parcours de consultation, récupérez le terme depuis la requête GET, puis transmettez-le au service avec les paramètres de pagination. Le service délègue au dépôt, qui applique le filtre avant LIMIT et OFFSET : ainsi, la page contient les résultats correspondant à la recherche, et non une page de contacts filtrée ensuite en PHP.
SELECT id, nom, courriel, telephone
FROM contacts
WHERE nom LIKE :terme
OR courriel LIKE :terme
ORDER BY nom, id
LIMIT :limite OFFSET :decalageDans ContactDepot, liez une valeur construite à partir du terme, par exemple « %» suivi du terme puis de « % », à :terme. Liez également limite et decalage comme entiers PDO. La recherche par sous-chaîne avec un joker au début peut empêcher l’utilisation d’un index B-tree ordinaire ; l’indexation évoquée dans les leçons précédentes ne garantit donc pas une accélération de cette requête. Conservez le tri stable par id après le nom : deux contacts portant le même nom ne changent pas d’ordre arbitrairement entre les pages.
Ajoutez la méthode de recherche au contrat DepotInterface et à ContactDepot, puis exposez-la par ContactService. Le contrôleur transmet le terme nettoyé au cas d’usage de consultation, et le rendu réaffiche ce terme dans le champ de recherche après l’avoir échappé pour le contexte HTML. N’ajoutez pas de nouvel accès direct à PDO dans le contrôleur : la recherche suit le même chemin que la liste.
Vérifier les résultats, pas seulement le statut HTTP
Un test de non-régression doit distinguer les résultats attendus des résultats qui ne doivent pas apparaître. Préparez dans la base de test un contact dont le nom correspond au terme, un contact dont le courriel correspond, un contact sans correspondance et, si possible, un contact qui ne correspondrait qu’à une comparaison trop permissive. Vérifiez aussi le comportement du terme vide et celui de la pagination.
<?php
declare(strict_types=1);
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
final class CarnetRechercheTest extends TestCase
{
public static function casDeRecherche(): array
{
return [
'nom correspondant' => ['Alice', ['Alice Martin']],
'courriel correspondant' => ['exemple.test', ['Sami Durand']],
'aucune correspondance' => ['introuvable-987', []],
];
}
#[DataProvider('casDeRecherche')]
public function testRechercheRetourneUniquementLesCorrespondances(
string $terme,
array $nomsAttendus
): void {
$reponse = $this->envoyerRequeteGet('/?recherche=' . urlencode($terme));
self::assertSame(200, $reponse->getStatusCode());
self::assertSame($nomsAttendus, $reponse->nomsDesContactsAffiches());
}
private function envoyerRequeteGet(string $chemin): object
{
throw new LogicException(
'Reliez cette méthode au client HTTP de test déjà configuré dans le projet.'
);
}
}Ce code illustre les assertions utiles, mais le client HTTP et l’accès aux noms rendus dépendent du harnais fonctionnel déjà installé dans votre projet. Remplacez uniquement cette adaptation par le client configuré dans CarnetFonctionnelTest ; ne laissez pas l’exception illustrative en place dans une suite destinée à être exécutée. Gardez les données isolées entre tests et vérifiez le contenu de la réponse, pas seulement son code 200.
Comparer les performances dans des conditions identiques
Utilisez le jeu de données contrôlé et le scénario de mesure établis dans les leçons de performance. Comparez la requête de recherche avant et après la correction envisagée, avec la même base, le même terme, la même taille de page et le même environnement. Répétez les mesures et comparez leurs médianes ; une exécution isolée peut varier à cause du cache, de la charge système ou de la connexion à la base.
<?php
declare(strict_types=1);
$mesuresAvant = [42.1, 39.8, 41.3, 44.0, 40.5];
$mesuresApres = [18.7, 19.4, 17.9, 20.2, 18.1];
$mediane = static function (array $mesures): float {
sort($mesures, SORT_NUMERIC);
$milieu = intdiv(count($mesures), 2);
return count($mesures) % 2 === 1
? $mesures[$milieu]
: ($mesures[$milieu - 1] + $mesures[$milieu]) / 2;
};
$avant = $mediane($mesuresAvant);
$apres = $mediane($mesuresApres);
$gainPourcent = (($avant - $apres) / $avant) * 100;
printf("Médiane avant : %.1f ms\n", $avant);
printf("Médiane après : %.1f ms\n", $apres);
printf("Gain mesuré : %.1f %%\n", $gainPourcent);Médiane avant : 41.3 ms
Médiane après : 18.7 ms
Gain mesuré : 54.7 %Ces valeurs sont un exemple de calcul, pas une promesse de résultat pour votre application. Consignez aussi le volume de données, le terme testé, la version de la requête et les conditions d’exécution. Si le temps baisse mais que les tests renvoient des contacts incorrects, l’optimisation est à rejeter ; si le résultat est correct mais que la mesure ne progresse pas, vérifiez le plan SQL et la pertinence de l’optimisation avant d’en ajouter une autre.
À retenir
- Faites circuler le terme depuis le contrôleur jusqu’au dépôt et appliquez le filtre avant la pagination.
- Liez le terme comme paramètre PDO ; ne construisez jamais la requête en concaténant l’entrée utilisateur.
- Testez les correspondances, les absences de résultat, le terme vide et les pages suivantes.
- Comparez plusieurs mesures prises dans des conditions identiques et confrontez le gain à la correction fonctionnelle.
Dans la leçon suivante, vous validerez l’ensemble des parcours et les critères de livraison du carnet privé.