- Laravel vous permet de tout implémenter, des moteurs de recherche simples avec AJAX aux recherches plein texte avancées utilisant Laravel Scout et des moteurs de recherche externes comme Algolia, Meilisearch ou Elasticsearch.
- Pour les recherches légères, le filtrage côté client avec Alpine.js ou avec des requêtes fetch natives évite de surcharger le serveur et améliore l'expérience utilisateur pour les petites listes.
- Laravel Scout centralise l'intégration avec différents moteurs de recherche et facilite le marquage des modèles comme indexables, la gestion des index et le lancement uniforme des requêtes.
- Le choix du moteur (SaaS, open source ou base de données) doit être basé sur le volume de données, la complexité des recherches et les exigences de performance et de maintenance du projet.
Lorsque vous commencez à travailler avec Laravel et que vous avez besoin d'un moteur de recherche en temps réel qui répond instantanément , il est facile de se perdre parmi la multitude d'approches possibles : AJAX avec fetch, jQuery, Alpine.js, Scout avec Algolia ou Meilisearch, filtrage côté client, etc. La bonne nouvelle, c'est que l'écosystème Laravel fournit déjà pratiquement tout ce dont vous avez besoin pour créer un moteur de recherche performant et rapide sans vous sentir submergé.
Dans cet article, vous apprendrez à implémenter différents types de recherche en temps réel avec Laravel , de l'autocomplétion AJAX classique à la recherche plein texte avec Laravel Scout et des moteurs de recherche comme Algolia, Meilisearch, la base de données elle-même, ou encore Elasticsearch. Vous découvrirez également des alternatives légères avec Alpine.js pour filtrer les données directement dans le navigateur, notamment pour les petits ensembles de données.
Qu'est-ce qu'une recherche en temps réel dans Laravel et comment fonctionne son principe de base ?
Le principe de la recherche en temps réel est le suivant : lorsqu’un utilisateur saisit du texte dans un champ , une requête est déclenchée et les résultats sont mis à jour sans rechargement de la page. Techniquement, cela repose sur trois éléments clés : le serveur Laravel, le JavaScript du navigateur et l’échange de données au format JSON.
D'une part, Laravel fait office de couche serveur chargée de recevoir les requêtes, d'interpréter les paramètres de recherche (le texte saisi), d'interroger la base de données et de renvoyer une réponse structurée, généralement au format JSON. Cette réponse peut indiquer le succès de la recherche, une erreur ou l'absence de résultats.
Côté serveur, JavaScript gère l'écoute des événements utilisateur sur le champ de recherche, l'envoi de requêtes asynchrones (AJAX) au serveur et l'affichage des données renvoyées sur la page sans rechargement complet du navigateur. Ceci peut être réalisé avec la fonction `fetch` native, jQuery AJAX ou de petites bibliothèques réactives comme Alpine.js.
Grâce à ce mécanisme de base, vous pouvez créer n'importe quoi, d'une simple fonction d'autocomplétion avec quelques enregistrements , à un moteur de recherche plein texte avancé avec pertinence, pagination et filtres, en vous appuyant sur des bibliothèques comme Laravel Scout et des moteurs de recherche externes optimisés pour la recherche.
Modèle, itinéraires et contrôleur pour un moteur de recherche temps réel basique
Avant de vous plonger dans JavaScript, vous devez vous assurer que la partie Laravel est bien organisée : un modèle Eloquent sur lequel effectuer la recherche, des routes claires et un contrôleur dédié à la gestion de la logique de recherche en temps réel.
La première étape consiste à créer un modèle Eloquent représentant la table dans laquelle vous allez effectuer la recherche. Prenons l'exemple d'une table de pays et d'un modèle très simple appelé « Pays » , sans horodatage et avec l'affectation groupée autorisée :
Exemple de modèle Eloquent minimal pour les recherches :
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Pais extends Model
{
use HasFactory;
protected $guarded = [];
public $timestamps = false;
}
Cela indique que le modèle Pais se trouve dans l'espace de noms standard de Laravel , hérite de Model et permet d'assigner n'importe quel champ avec create() en laissant le tableau protégé vide. En désactivant les horodatages avec public $timestamps = false, vous évitez les problèmes si la table ne contient pas les colonnes created_at et updated_at.
L'étape suivante consiste à définir les routes qui géreront à la fois l'affichage du moteur de recherche et les requêtes AJAX . Un schéma très courant combine une route GET pour afficher la vue et une route POST conçue pour recevoir les requêtes en temps réel :
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BuscadorController;
Route::get('/', function () {
return view('welcome');
});
Route::get('buscador', [BuscadorController::class, 'index']);
Route::post('buscador', [BuscadorController::class, 'buscar']);
La route racine affiche une page d'accueil, tandis que l' URL /search est réservée à la fonctionnalité de recherche . La méthode index() du contrôleur affiche le formulaire et le champ de recherche, tandis que la méthode search() traite les requêtes asynchrones envoyées par le navigateur.
Dans le contrôleur, vous pouvez implémenter un modèle très pratique : préparer un tableau de réponse par défaut en cas d’erreur et ne le remplacer que lorsqu’il s’agit effectivement d’une requête AJAX valide et que la requête s’exécute sans problème.
Le contrôleur pourrait avoir une structure similaire à celle-ci :
namespace App\Http\Controllers;
use App\Models\Pais;
use Illuminate\Http\Request;
class BuscadorController extends Controller
{
public function index()
{
return view('welcome');
}
public function buscar(Request $request)
{
$response = [
'success' => false,
'message' => 'Hubo un error',
];
if ($request->ajax()) {
$data = Pais::where('nombre', 'like', $request->texto.'%')
->take(10)
->get();
$response = [
'success' => true,
'message' => 'Consulta correcta',
'data' => $data,
];
}
return response()->json($response);
}
}
À ce stade, le cycle backend est complet : réception d’une requête AJAX, vérification de son statut AJAX, requête avec `where like`, et limitation du nombre de résultats à un nombre raisonnable avec `take(10)` afin d’éviter la surcharge de la base de données. La réponse est toujours envoyée au format JSON, ce qui simplifie considérablement le travail du frontend.
Vue Blade et récupération JavaScript pour la recherche réactive
Une fois le modèle, les routes et le contrôleur prêts, il est temps de construire la partie visible : un formulaire avec un champ de recherche et un bloc pour afficher les résultats , ainsi que le code JavaScript chargé d’effectuer les requêtes en arrière-plan.
La vue Blade peut être très simple, s'appuyant sur le jeton CSRF que Laravel injecte pour valider les requêtes POST et sur un champ de recherche facile à utiliser :
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<strong><meta name="csrf-token" content="{{ csrf_token() }}"></strong>
<title>Laravel</title>
</head>
<body>
<form action="" method="post">
<input type="search" name="texto" id="buscar">
</form>
<div id="resultado"></div>
<script>
window.addEventListener('load', function () {
const buscar = document.getElementById('buscar');
const resultado = document.getElementById('resultado');
buscar.addEventListener('keyup', function () {
fetch('/buscador', {
method: 'post',
body: JSON.stringify({ texto: buscar.value }),
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.head.querySelector('[name~="csrf-token"][content]').content,
},
})
.then(response => response.json())
.then(data => {
let html = '';
if (data.success) {
html += '<ul>';
for (let i in data.data) {
html += '<li>' + data.data[i].nombre + '</li>';
}
html += '<ul>';
} else {
html += 'No existen resultados';
}
resultado.innerHTML = html;
});
});
});
</script>
</body>
</html>
Dans cet exemple, le script écoute l'événement keyup du champ de recherche ; chaque frappe déclenche donc une requête fetch vers le chemin /search. Le texte actuel du champ est envoyé au format JSON, et des en-têtes clés tels que X-Requested-With sont inclus pour indiquer qu'il s'agit d'une requête AJAX, ainsi que le jeton CSRF pour contourner la protection native de Laravel.
À réception de la réponse, celle-ci est transformée en JSON et une courte liste HTML contenant les résultats est générée dynamiquement . Si la requête ne renvoie aucune donnée, un message tel que « Aucun résultat trouvé » s'affiche. Tout ceci se déroule sans rechargement de la page, de manière intuitive pour l'utilisateur.
Ce modèle peut être affiné davantage grâce à de petits détails d'expérience utilisateur, tels que l'ajout d'un délai entre les frappes au clavier, l'affichage d'un indicateur de chargement ou la gestion des erreurs réseau pour éviter que l'interface ne paraisse figée en cas de problème.
Recherche en direct avec Laravel et AJAX utilisant jQuery
Bien que la fonction `fetch` ait gagné en popularité, jQuery AJAX reste très prisé dans les projets existants ou les équipes qui l'ont déjà intégrée. Le principe est le même : capturer la saisie de l'utilisateur, effectuer une requête asynchrone et actualiser le DOM.
Un flux de travail typique avec jQuery dans Laravel pour une recherche en direct comprend généralement ces étapes de base : définir une route spécifique, créer un contrôleur dédié, construire la vue Blade avec l’entrée de recherche et enfin ajouter le code jQuery qui déclenche AJAX au fur et à mesure de la saisie.
Le processus fonctionne ainsi : lorsque l’utilisateur commence à saisir du texte, jQuery envoie une requête au serveur contenant la chaîne de recherche. Laravel filtre les informations dans la base de données, renvoie un objet JSON avec les résultats correspondants, et jQuery met à jour un conteneur HTML sur la page pour refléter ces correspondances, le tout en quelques millisecondes.
L'avantage d'utiliser jQuery est qu'il simplifie considérablement la syntaxe AJAX et la rend très lisible si vous utilisez déjà cette bibliothèque dans votre projet. Cependant, il introduit une dépendance supplémentaire qui peut s'avérer inutile si vous maîtrisez JavaScript moderne et les fonctionnalités natives de récupération de données.
Filtrage et recherche en temps réel côté client avec Alpine.js
Lorsque le volume de données à afficher est relativement faible (par exemple, moins de 50 éléments ), il n'est pas toujours judicieux de mettre en place un système de recherche complexe. Dans ce cas, une solution très pratique consiste à filtrer directement dans le navigateur à l'aide d'Alpine.js , sans effectuer de requêtes au serveur pendant la saisie.
L'idée est de précalculer une chaîne de recherche pour chaque élément (par exemple, nom, description et catégorie en minuscules), de l'enregistrer dans un attribut data-search-text et de laisser Alpine.js gérer l'affichage ou le masquage des éléments en fonction du texte saisi dans un champ de recherche.
Le composant Alpine.js peut avoir une structure similaire à celle-ci : filterItems
{
search: '',
hasResults: true,
selectedValue: '',
init() {
this.$watch('search', () => this.filterItems());
this.$nextTick(() => this.$refs.searchInput?.focus());
},
filterItems() {
const searchLower = this.search.toLowerCase().trim();
const cards = this.$el.querySelectorAll('.item-card');
let visibleCount = 0;
cards.forEach(card => {
const text = card.dataset.searchText || '';
const isVisible = searchLower === '' || text.includes(searchLower);
card.style.display = isVisible ? '' : 'none';
if (isVisible) visibleCount++;
});
this.hasResults = visibleCount > 0;
},
}
Dans cette vue, chaque carte ou ligne de données posséderait un attribut data-search-text dont le texte serait déjà préparé en minuscules ; le filtre se réduit donc à un include() en JavaScript, très rapide pour les listes courtes :
<input type="search" x-model="search" x-ref="searchInput" placeholder="Buscar..." />
<div>
<div class="item-card" data-search-text="formulario contacto simple">
<h3>Formulario de contacto</h3>
<p>Formulario de contacto simple</p>
</div>
</div>
De plus, vous pouvez afficher un bloc d'état vide uniquement lorsqu'il n'y a aucun résultat pour le terme de recherche actuel , invitant l'utilisateur à modifier le texte ou à effacer le champ avec un bouton qui réinitialise simplement la recherche à une chaîne vide.
Cette approche présente des avantages indéniables : aucune requête serveur n’est effectuée pendant la recherche , l’interaction est quasi instantanée et la logique reste très locale, facilitant ainsi le débogage. Elle est idéale pour les sélecteurs rapides, les fenêtres modales de sélection d’articles ou les petits catalogues intégrés à une page Laravel.
Laravel Scout : Recherche plein texte avec des moteurs spécialisés
Lorsque les besoins en recherche plein texte deviennent critiques et que vous devez impérativement utiliser une solution rapide, pertinente et évolutive , Laravel Scout s'impose comme la solution idéale dans Laravel. Scout est une couche d'intégration qui vous permet de connecter facilement vos modèles Eloquent à des moteurs de recherche tels qu'Algolia, Meilisearch, votre base de données, vos collections en mémoire, ou même Elasticsearch via des contrôleurs externes.
Pour commencer à utiliser Scout, la méthode habituelle consiste à créer un nouveau projet Laravel ou à réutiliser un projet existant , à le lancer avec Docker (par exemple, avec Laravel Sail), puis à installer la bibliothèque avec Composer. Une fois cette étape terminée, il suffit de publier le fichier de configuration `scout.php` et d'ajuster les variables d'environnement en fonction du pilote souhaité.
Un flux de travail typique consiste à installer Scout avec Composer, à publier sa configuration et à activer la file d'indexation avec `SCOUT_QUEUE=true` dans le fichier `.env` afin que les opérations gourmandes en ressources soient traitées en arrière-plan, améliorant ainsi les temps de réponse de l'application. De plus, vous devez vous assurer que `DB_HOST` pointe vers la base de données que vous utilisez, ce qui est particulièrement important si vous utilisez des conteneurs Docker.
Pour qu'un modèle puisse participer aux recherches Scout, il doit être explicitement marqué comme indexable en ajoutant le trait `Searchable` . Par exemple, si vous avez un modèle `Train` qui représente un tableau de trains avec un champ `title`, vous pouvez le définir comme ceci :
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Train extends Model
{
use Searchable;
protected $fillable = ['title'];
public function searchableAs()
{
return 'trains_index';
}
}
La méthode `searchableAs` permet de personnaliser le nom de l'index dans le moteur de recherche , au lieu d'utiliser le nom par défaut issu du modèle. Scout gère ensuite la synchronisation des opérations de création, de mise à jour et de suppression avec l'index distant ou local, selon le pilote choisi.
Laravel Scout avec Algolia : Recherche SaaS ultra-rapide
Algolia est un service SaaS qui permet d'effectuer des recherches très rapides et pertinentes sur de grands volumes de données . Il propose une interface web pour la gestion des index, des règles de pertinence, des synonymes, etc., et s'intègre parfaitement à Laravel via Scout et le client PHP officiel.
Pour utiliser Algolia avec Scout, vous devrez installer son client PHP via Composer, enregistrer vos identifiants dans le fichier `.env` (ID d'application et clé API d'administration) et configurer `SCOUT_DRIVER=algolia` pour indiquer à Scout d'utiliser ce moteur. Vous trouverez l'ID d'application et la clé d'administration sur le tableau de bord Algolia.
Une fois l'environnement configuré, vous pouvez utiliser des méthodes comme Train::search('text')->paginate(6) directement dans vos contrôleurs pour effectuer des recherches sur les champs indexés, en recevant des résultats au format Eloquent paginé prêts à être transmis à une vue Blade.
Par exemple , vous pourriez avoir un contrôleur d'index qui liste tous les trains ou effectue une recherche si un paramètre titlesearch est transmis, et une méthode create pour insérer de nouveaux trains dans l'index :
public function index(Request $request)
{
if ($request->has('titlesearch')) {
$trains = Train::search($request->titlesearch)->paginate(6);
} else {
$trains = Train::paginate(6);
}
return view('Train-search', compact('trains'));
}
public function create(Request $request)
{
$this->validate($request, ['title' => 'required']);
Train::create($request->all());
return back();
}
Dans la vue correspondante, vous pouvez combiner un formulaire d'ajout de nouveaux trains et un autre formulaire GET avec un champ de recherche par titre qui lance la recherche lors de la soumission. Il vous suffit ensuite de parcourir la liste des trains et d'afficher leurs champs dans un tableau, en tirant parti des liens de pagination générés par Laravel.
Explorez avec Meilisearch, base de données et collections
Si vous préférez éviter les services externes, Meilisearch est un moteur de recherche open source que vous pouvez déployer localement ou sur votre infrastructure. Scout s'intègre à Meilisearch de manière très similaire à Algolia : il suffit de modifier le pilote et d'ajouter les variables MEILISEARCH_HOST et MEILISEARCH_KEY au fichier .env.
Pour l'utiliser, installez le client PHP Meilisearch, définissez SCOUT_DRIVER=meilisearch et indiquez à MEILISEARCH_HOST l'URL de l'instance (par exemple, http://127.0.0.1:7700). Si vous possédez déjà des enregistrements, vous pouvez les indexer avec la commande php artisan scout:import "App\Models\Train" afin qu'ils soient disponibles pour le moteur.
Pour les applications de petite ou moyenne taille, vous pouvez également opter pour le pilote de base de données de Scout , qui exploite les index de texte intégral et les instructions LIKE de votre base de données MySQL ou PostgreSQL. Dans ce cas, aucun service externe n'est requis ; il suffit de définir SCOUT_DRIVER=database pour que Scout utilise la base de données elle-même comme moteur de recherche.
Une autre option intéressante est le pilote de collection, qui fonctionne avec les collections Eloquent en mémoire . Ce moteur filtre les résultats à l'aide de la clause `where` et des méthodes de filtrage de collection, et il est compatible avec toutes les bases de données prises en charge par Laravel. Vous pouvez l'activer avec `SCOUT_DRIVER=collection` ou en modifiant le fichier de configuration de Scout pour une configuration plus spécifique.
Intégration avec Elasticsearch via Explorer
Si vos besoins de recherche impliquent le traitement de volumes massifs de données et l'analyse en temps réel , Elasticsearch est une solution incontournable. Dans l'écosystème Laravel, une méthode moderne pour l'intégrer à Scout consiste à utiliser le contrôleur Explorer, qui sert d'interface entre vos modèles et un cluster Elasticsearch.
Cela implique généralement l'utilisation de Docker et d'un fichier docker-compose complet qui lance, outre les services habituels (Laravel, MySQL, Redis, Miilisearch, etc.), des conteneurs Elasticsearch et Kibana . Ensuite, vous installez le paquet jeroen-g/explorer via Composer et publiez son fichier de configuration pour spécifier les modèles à indexer.
Dans le fichier config/explorer.php, vous pouvez enregistrer vos modèles sous la clé indexes, par exemple en ajoutant App\Models\Train::class . De plus, vous devez modifier le pilote de Scout à Elasticsearch dans le fichier .env avec SCOUT_DRIVER=elastic afin que tout pointe vers Elasticsearch.
Dans le modèle Train, vous devez implémenter l'interface Explored et redéfinir la méthode mappableAs , qui définit la correspondance entre les champs à envoyer à l'index. Voici un exemple minimal :
use JeroenG\Explorer\Application\Explored;
use Laravel\Scout\Searchable;
class Train extends Model implements Explored
{
use Searchable;
protected $fillable = ['title'];
public function mappableAs(): array
{
return [
'id' => $this->id,
'title' => $this->title,
];
}
}
À partir de là, vous pouvez lancer des recherches sur Elasticsearch en utilisant la même interface Scout , en bénéficiant de temps de réponse très courts et de toute la puissance de requête de ce moteur, mais sans quitter l'écosystème Laravel.
Avec toutes ces approches — de l'autocomplétion basique avec fetch ou jQuery, au filtrage frontend avec Alpine.js, en passant par les recherches plein texte avec Laravel Scout et divers pilotes — Laravel vous offre un vaste éventail d'options pour implémenter des recherches en temps réel adaptées à la taille de votre projet, aux performances dont vous avez besoin et à l'infrastructure que vous êtes prêt à maintenir.