- Met Laravel kunt u alles implementeren, van eenvoudige zoekmachines met AJAX tot geavanceerde zoekopdrachten in volledige tekst met Laravel Scout en externe zoekmachines zoals Algolia, Meilisearch of Elasticsearch.
- Bij lichte zoekopdrachten voorkomt het filteren op de frontend met Alpine.js of met native fetch requests dat de server overbelast raakt en verbetert de gebruikerservaring bij kleine lijsten.
- Laravel Scout centraliseert de integratie met verschillende zoekmachines en maakt het eenvoudig om modellen als doorzoekbaar te markeren, indexen te beheren en query's op uniforme wijze te starten.
- De keuze van de engine (SaaS, open source of database) moet gebaseerd zijn op de hoeveelheid data, de complexiteit van de zoekopdrachten en de prestatie- en onderhoudsvereisten van het project.
Wanneer je met Laravel begint te werken en een realtime zoekmachine nodig hebt die direct reageert , is het gemakkelijk om de weg kwijt te raken in de duizenden mogelijke benaderingen: AJAX met fetch, jQuery, Alpine.js, Scout met Algolia of Meilisearch, frontend-filtering, enzovoort. Het goede nieuws is dat het Laravel-ecosysteem al praktisch alles biedt wat je nodig hebt om een soepele en snelle zoekmachine te bouwen zonder overweldigd te raken.
In dit artikel leer je hoe je verschillende soorten realtime zoeken in Laravel implementeert , van klassieke AJAX-autocomplete tot full-text zoeken met Laravel Scout en zoekmachines zoals Algolia, Meilisearch, de database zelf of zelfs Elasticsearch. Je verkent ook lichtgewicht alternatieven met Alpine.js voor het filteren van gegevens direct in de browser bij het werken met kleine datasets.
Wat is een realtime zoekopdracht in Laravel en hoe werkt het in de basis?
Het idee achter realtime zoeken is dat, zodra de gebruiker iets in een tekstveld typt , er een zoekopdracht wordt uitgevoerd en de resultaten worden bijgewerkt zonder de pagina opnieuw te laden. Technisch gezien omvat dit drie belangrijke componenten: de Laravel-backend, de JavaScript van de browser en de gegevensuitwisseling in JSON-formaat.
Enerzijds fungeert Laravel als de serverlaag die verantwoordelijk is voor het ontvangen van verzoeken, het interpreteren van zoekparameters (de ingevoerde tekst), het opvragen van gegevens uit de database en het retourneren van een gestructureerd antwoord, meestal in JSON-formaat. Dit antwoord kan aangeven of de zoekopdracht succesvol is, een foutmelding geeft of dat er geen resultaten zijn gevonden.
Aan de andere kant zorgt JavaScript ervoor dat gebruikersgebeurtenissen in het zoekveld worden opgevangen, asynchrone verzoeken (AJAX) naar de backend worden verzonden en de geretourneerde gegevens op de pagina worden weergegeven zonder dat de browser volledig hoeft te worden vernieuwd. Dit kan worden gedaan met native fetch, jQuery AJAX of kleine reactieve bibliotheken zoals Alpine.js.
Met dit basismechanisme kun je alles bouwen, van een simpele autocomplete met een paar resultaten tot een geavanceerde full-text zoekmachine met relevantie, paginering en filters, gebruikmakend van bibliotheken zoals Laravel Scout en externe zoekmachines die geoptimaliseerd zijn voor zoekopdrachten.
Model, routes en controller voor een eenvoudige realtime zoekmachine
Voordat je je in JavaScript verdiept, moet de Laravel-kant goed georganiseerd zijn: een Eloquent-model om op te zoeken, duidelijke routes en een controller die speciaal is ontworpen om de zoeklogica in realtime af te handelen.
De eerste stap is het hebben van een Eloquent-model dat de tabel representeert waarin je gaat zoeken. Stel je een tabel met landen voor en een heel eenvoudig model genaamd Country , zonder tijdstempels en met de mogelijkheid tot bulktoewijzing:
Voorbeeld van een minimaal Eloquent-model voor zoekopdrachten :
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;
}
Dit geeft aan dat het Pais-model zich in de standaard Laravel-namespace bevindt , overerft van Model en het toestaat om elk veld toe te wijzen met create() door de beveiligde array leeg te laten. Door timestamps uit te schakelen met public $timestamps = false, voorkomt u problemen als de tabel geen created_at- en updated_at-kolommen heeft.
De volgende stap is het definiëren van de routes die zowel de weergave van de zoekmachine als de AJAX-verzoeken afhandelen . Een veelgebruikt schema combineert een GET-route voor het weergeven van de pagina met een POST-route die is ontworpen om realtime zoekopdrachten te ontvangen:
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']);
De hoofdroute geeft een welkomstpagina weer, terwijl de URL /search is gereserveerd voor de zoekfunctionaliteit . De index()-methode van de controller toont het formulier en het zoekveld, terwijl de search()-methode asynchrone verzoeken verwerkt die vanuit de browser worden verzonden.
In de controller kun je een zeer praktisch patroon implementeren: maak een standaard antwoordarray aan voor het geval er een fout optreedt en overschrijf deze pas wanneer het daadwerkelijk een geldig AJAX-verzoek is en de query zonder problemen wordt uitgevoerd.
De controller zou een structuur kunnen hebben die hierop lijkt:
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);
}
}
Op dit punt heb je de complete backend-cyclus: een inkomend AJAX-verzoek, verificatie dat het inderdaad een AJAX-verzoek is, een query met `where like` en het beperken van het aantal resultaten tot een redelijk aantal met `take(10)` om overbelasting van de database te voorkomen. Het antwoord wordt altijd in JSON-formaat verzonden, wat het werk van de frontend aanzienlijk vereenvoudigt.
Blade view en JavaScript fetch voor reactief zoeken
Nu het model, de routes en de controller klaar zijn, is het tijd om het zichtbare gedeelte te bouwen: een formulier met een zoekveld en een blok om de resultaten weer te geven , plus de JavaScript-code die verantwoordelijk is voor het uitvoeren van de verzoeken op de achtergrond.
De Blade-weergave kan heel eenvoudig zijn, door gebruik te maken van het CSRF-token dat Laravel injecteert om POST-verzoeken te valideren en van een gebruiksvriendelijk zoekveld:
<!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>
In dit voorbeeld luistert het script naar de `keyup`-gebeurtenis op het zoekveld , waardoor elke toetsaanslag een fetch-verzoek naar het pad `/search` activeert. De huidige tekst van het veld wordt verzonden in JSON-formaat, en belangrijke headers zoals `X-Requested-With` worden toegevoegd om aan te geven dat het om AJAX gaat, samen met het CSRF-token om de ingebouwde beveiliging van Laravel te omzeilen.
Wanneer het antwoord binnenkomt, wordt het omgezet naar JSON en wordt er dynamisch een kleine HTML-lijst met de resultaten gegenereerd , of wordt er een bericht zoals "Geen resultaten gevonden" weergegeven als de zoekopdracht geen gegevens oplevert. Dit alles gebeurt zonder de pagina opnieuw te laden, op een natuurlijke manier voor de gebruiker.
Dit patroon kan verder worden verfijnd met kleine UX-details, zoals het toevoegen van een vertraging tussen toetsaanslagen, het weergeven van een laadindicator of het afhandelen van netwerkfouten om te voorkomen dat de interface vastloopt wanneer er iets misgaat.
Live zoeken met Laravel en AJAX met behulp van jQuery
Hoewel fetch aanzienlijk aan populariteit heeft gewonnen, blijft jQuery AJAX erg geliefd in bestaande projecten of teams die het al hebben geïntegreerd. Het principe is exact hetzelfde: vastleggen wat de gebruiker typt, een asynchroon verzoek uitvoeren en de DOM vernieuwen.
Een typische workflow met jQuery in Laravel voor een live zoekfunctie omvat meestal deze basisstappen: een specifieke route definiëren, een aparte controller maken, de Blade-view met het zoekveld bouwen en tot slot de jQuery-code toevoegen die AJAX activeert zodra er iets wordt ingevoerd.
Het proces werkt als volgt: zodra de gebruiker begint te typen, stuurt jQuery een query naar de server met de zoekterm. Laravel filtert de informatie in de database, retourneert een JSON-object met de overeenkomende resultaten en jQuery werkt een HTML-container op de pagina bij om de overeenkomsten weer te geven, dit alles binnen enkele milliseconden.
Het voordeel van het gebruik van jQuery is dat het de AJAX-syntaxis aanzienlijk vereenvoudigt en zeer gemakkelijk te lezen is als je de bibliotheek al in je project hebt. Het introduceert echter een extra afhankelijkheid die mogelijk niet nodig is als je met moderne JavaScript en native fetch-functionaliteit kunt werken.
Realtime filteren en zoeken op de frontend met Alpine.js
Wanneer de weer te geven gegevens relatief klein zijn (bijvoorbeeld minder dan 50 items ), is het niet altijd zinvol om een backend met complexe zoekfuncties op te zetten. In deze gevallen is het erg handig om direct in de browser te filteren met Alpine.js , zonder dat er tijdens het typen verzoeken naar de server worden gestuurd.
Het idee is om voor elk item vooraf een zoekstring te berekenen (bijvoorbeeld naam, beschrijving en categorie in kleine letters), deze op te slaan in een data-search-text-attribuut, en Alpine.js vervolgens te laten bepalen of items worden weergegeven of verborgen op basis van de tekst die in een zoekveld is ingevoerd.
De Alpine.js-component kan een structuur hebben die hierop lijkt: 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;
},
}
In de weergave zou elke kaart of rij met gegevens een data-search-text-attribuut hebben met de tekst al in kleine letters , waardoor het filter in JavaScript wordt gereduceerd tot een includes()-functie, wat erg snel is voor korte lijsten:
<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>
Daarnaast kunt u een leeg statusblok weergeven, maar alleen wanneer er geen resultaten zijn voor de huidige zoekterm . De gebruiker wordt dan uitgenodigd om de tekst aan te passen of het veld leeg te maken met een knop die de zoekopdracht eenvoudigweg terugzet naar een lege tekenreeks.
Deze aanpak heeft duidelijke voordelen: er worden geen serveraanroepen gedaan tijdens het zoeken , de interactie is vrijwel direct en de logica blijft zeer lokaal en is gemakkelijk te debuggen. Het is perfect voor snelle selectors, itemselectiemodals of kleine catalogi die in een Laravel-pagina zijn ingebed.
Laravel Scout: zoeken in volledige tekst met gespecialiseerde engines
Wanneer het erop aankomt en je snel, relevant en schaalbaar full-text zoeken nodig hebt , is Laravel Scout de logische keuze in Laravel. Scout is een integratielaag waarmee je je Eloquent-modellen eenvoudig kunt verbinden met zoekmachines zoals Algolia, Meilisearch, je database, in-memory collecties of zelfs Elasticsearch via externe controllers.
Om met Scout aan de slag te gaan, is de gebruikelijke aanpak het aanmaken van een nieuw Laravel-project of het hergebruiken van een bestaand project , het lanceren ervan met Docker (bijvoorbeeld met Laravel Sail) en vervolgens het installeren van de bibliotheek met Composer. Zodra dat is gedaan, publiceer je het configuratiebestand scout.php en pas je de omgevingsvariabelen aan op basis van de driver die je wilt gebruiken.
Een typische workflow is om Scout te installeren met Composer, de configuratie te publiceren en de indexeringswachtrij in te schakelen met `SCOUT_QUEUE=true` in het `.env`-bestand, zodat resource-intensieve bewerkingen op de achtergrond worden verwerkt, wat de reactietijden van de applicatie verbetert. Daarnaast moet u ervoor zorgen dat `DB_HOST` verwijst naar de database die u gebruikt, wat vooral belangrijk is als u Docker-containers gebruikt.
Om een model te laten deelnemen aan Scout-zoekopdrachten, moet het expliciet als doorzoekbaar worden gemarkeerd door de trait `Searchable` toe te voegen . Als je bijvoorbeeld een `Train`-model hebt dat een tabel met treinen vertegenwoordigt met een veld `title`, kun je het als volgt definiëren:
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';
}
}
Met de `searchableAs`-methode kunt u de indexnaam in de zoekmachine aanpassen , in plaats van de standaardnaam te gebruiken die is afgeleid van het model. Vervolgens zorgt Scout voor de synchronisatie van aanmaak-, update- en verwijderingsbewerkingen met de externe of lokale index, afhankelijk van de gekozen driver.
Laravel Scout met Algolia: razendsnelle SaaS-zoekfunctie
Algolia is een SaaS-service die zich richt op het leveren van zeer snelle en relevante zoekopdrachten in grote hoeveelheden data . Het biedt een webpaneel voor het beheren van indexen, relevantieregels, synoniemen en meer, en integreert naadloos met Laravel via Scout en de officiële PHP-client.
Om Algolia met Scout te gebruiken, moet je de PHP-client installeren met Composer, je inloggegevens registreren in het .env-bestand (applicatie-ID en beheerders-API-sleutel) en SCOUT_DRIVER=algolia configureren om Scout te laten weten dat deze engine gebruikt moet worden. Je kunt zowel de applicatie-ID als de beheerderssleutel vinden in het Algolia-dashboard.
Zodra de omgeving is ingesteld, kunt u methoden zoals Train::search('text')->paginate(6) rechtstreeks in uw controllers gebruiken om zoekopdrachten uit te voeren op de geïndexeerde velden. De resultaten worden vervolgens in gepagineerd Eloquent-formaat weergegeven, klaar om te worden doorgegeven aan een Blade-weergave.
Je zou bijvoorbeeld een indexcontroller kunnen hebben die alle treinen weergeeft of een zoekopdracht uitvoert als de parameter titlesearch wordt doorgegeven, en een create-methode om nieuwe treinen aan de index toe te voegen:
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();
}
In de bijbehorende weergave kunt u een formulier voor het toevoegen van nieuwe treinen combineren met een ander GET-formulier met een zoekveld voor de titel, dat de zoekopdracht activeert bij het verzenden van het formulier. Vervolgens doorloopt u eenvoudig de verzameling treinen en toont u hun velden in een tabel, waarbij u gebruikmaakt van de pagineringslinks die door Laravel worden gegenereerd.
Verkennen met Meilisearch, database en collecties
Als u liever geen externe services gebruikt, is Meilisearch een open-source zoekmachine die u lokaal of op uw eigen infrastructuur kunt implementeren. Scout integreert met Meilisearch op een vergelijkbare manier als Algolia, simpelweg door de driver aan te passen en de variabelen MEILISEARCH_HOST en MEILISEARCH_KEY toe te voegen aan het .env-bestand.
Om het te gebruiken, installeer je de Meilisearch PHP-client, stel je SCOUT_DRIVER=meilisearch in en verwijs je MEILISEARCH_HOST naar de URL van de instantie (bijvoorbeeld http://127.0.0.1:7700). Als je al records hebt, kun je deze indexeren met het commando php artisan scout:import "App\Models\Train" zodat de engine ze beschikbaar heeft.
Voor kleinere of middelgrote applicaties kunt u ook kiezen voor de database-driver van Scout , die gebruikmaakt van full-text indexen en LIKE-instructies op uw MySQL- of PostgreSQL-database. In dit geval hebt u geen externe service nodig; stel eenvoudigweg SCOUT_DRIVER=database in, zodat Scout de database zelf als zoekmachine gebruikt.
Een andere interessante optie is de collection driver, die werkt met in-memory Eloquent-collecties . Deze engine filtert resultaten met behulp van `where` en collectiefiltermethoden, en is compatibel met elke database die door Laravel wordt ondersteund. Je kunt hem activeren met `SCOUT_DRIVER=collection` of door het Scout-configuratiebestand aan te passen als je iets specifiekers wilt.
Integratie met Elasticsearch met behulp van Explorer
Als je zoekopdrachten betrekking hebben op het werken met enorme hoeveelheden data en realtime analyses , is Elasticsearch een klassieker. Binnen het Laravel-ecosysteem is een moderne manier om het met Scout te integreren het gebruik van de Explorer-controller, die fungeert als een brug tussen je modellen en een Elasticsearch-cluster.
Dit houdt doorgaans in dat je werkt met Docker en een uitgebreid docker-compose-bestand dat, naast de gebruikelijke services (Laravel, MySQL, Redis, Meilisearch, enz.), ook Elasticsearch- en Kibana-containers start . Vervolgens installeer je het jeroen-g/explorer-pakket via Composer en publiceer je het configuratiebestand om aan te geven welke modellen geïndexeerd moeten worden.
In het bestand config/explorer.php kunt u uw modellen registreren onder de sleutel indexes, bijvoorbeeld door App\Models\Train::class toe te voegen . Daarnaast wijzigt u de driver van Scout naar Elasticsearch in het .env-bestand met SCOUT_DRIVER=elastic, zodat alles naar Elasticsearch verwijst.
Binnen het Train-model moet je de Explored-interface implementeren en de mappableAs- methode overschrijven . Deze methode definieert de map met velden die naar de index moeten worden verzonden. Een minimaal voorbeeld is:
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,
];
}
}
Vanuit hier kunt u zoekopdrachten uitvoeren in Elasticsearch met dezelfde Scout-interface , waarbij u profiteert van zeer korte responstijden en de volledige zoekkracht van deze engine, zonder het Laravel-ecosysteem te verlaten.
Met al deze benaderingen – van eenvoudige autocomplete met fetch of jQuery, tot frontend-filtering met Alpine.js, tot full-text zoeken met Laravel Scout en diverse drivers – biedt Laravel een enorm scala aan opties om realtime zoekopdrachten te implementeren die zijn afgestemd op de omvang van je project, de prestaties die je nodig hebt en de infrastructuur die je bereid bent te onderhouden.