- Laravel vă permite să implementați totul, de la motoare de căutare simple cu AJAX până la căutări avansate de text complet folosind Laravel Scout și motoare de căutare externe precum Algolia, Meilisearch sau Elasticsearch.
- Pentru căutări ușoare, filtrarea pe frontend cu Alpine.js sau cu cereri native de fetch evită supraîncărcarea serverului și îmbunătățește experiența utilizatorului în listele mici.
- Laravel Scout centralizează integrarea cu diferite motoare de căutare și facilitează marcarea modelelor ca fiind căutabile, gestionarea indexurilor și lansarea uniformă a interogărilor.
- Alegerea motorului (SaaS, open source sau bază de date) ar trebui să se bazeze pe volumul de date, complexitatea căutărilor și cerințele de performanță și întreținere ale proiectului.
Când începi să lucrezi cu Laravel și ai nevoie de un motor de căutare în timp real care răspunde instantaneu , este ușor să te pierzi printre o mie de abordări posibile: AJAX cu fetch, jQuery, Alpine.js, Scout cu Algolia sau Meilisearch, filtrare frontend etc. Vestea bună este că ecosistemul Laravel oferă deja practic tot ce ai nevoie pentru a construi un motor de căutare fluid și rapid, fără a te simți copleșit.
În acest articol, veți învăța cum să implementați diferite tipuri de căutare în timp real în Laravel , de la autocompletarea clasică AJAX la căutări full-text folosind Laravel Scout și motoare de căutare precum Algolia, Meilisearch, baza de date în sine sau chiar Elasticsearch. De asemenea, veți explora alternative ușoare cu Alpine.js pentru filtrarea datelor direct în browser atunci când lucrați cu seturi de date mici.
Ce este o căutare în timp real în Laravel și cum funcționează elementele de bază?
Ideea din spatele căutării în timp real este că, pe măsură ce utilizatorul tastează într-un câmp de text , se declanșează o interogare, iar rezultatele sunt actualizate fără a reîncărca pagina. Din punct de vedere tehnic, aceasta implică trei componente cheie: backend-ul Laravel, JavaScript-ul browserului și schimbul de date în format JSON.
Pe de o parte, Laravel acționează ca stratul serverului responsabil pentru primirea cererilor, interpretarea parametrilor de căutare (textul introdus), interogarea bazei de date și returnarea unui răspuns structurat, de obicei în format JSON. Acest răspuns poate indica succes, eroare sau faptul că nu s-au găsit rezultate.
La celălalt capăt, JavaScript se ocupă de ascultarea evenimentelor utilizatorului pe inputul de căutare, trimiterea de cereri asincrone (AJAX) către backend și redarea datelor returnate pe pagină fără a fi necesară o reîmprospătare completă a browserului. Acest lucru se poate face cu fetch nativ, jQuery AJAX sau biblioteci reactive mici, cum ar fi Alpine.js.
Cu acest mecanism de bază puteți construi orice, de la o simplă completare automată cu câteva înregistrări , până la un motor de căutare avansat full-text cu relevanță, paginare și filtre, bazându-vă pe biblioteci precum Laravel Scout și motoare de căutare externe optimizate pentru căutări.
Model, rute și controler pentru un motor de căutare în timp real de bază
Înainte de a aprofunda JavaScript, este nevoie ca partea Laravel să fie bine organizată: un model Eloquent pe care să se poată căuta, rute clare și un controller dedicat gestionării logicii de căutare în timp real.
Primul pas este să aveți un model Eloquent care să reprezinte tabelul în care veți căuta. Imaginați-vă un tabel de țări și un model foarte simplu numit Țară , fără timestamp-uri și cu atribuire în bloc permisă:
Exemplu de model Eloquent minimal pentru căutări :
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;
}
Aceasta indică faptul că modelul Pais se află în spațiul de nume standard Laravel , moștenește de la Model și permite atribuirea oricărui câmp cu create() lăsând matricea protejată goală. Prin dezactivarea timestamp-urilor cu public $timestamps = false, evitați problemele dacă tabelul nu are coloanele created_at și updated_at.
Următorul pas este definirea rutelor care vor gestiona atât afișarea motorului de căutare, cât și cererile AJAX . O schemă foarte comună combină o rută GET pentru afișarea vizualizării și o rută POST concepută pentru a primi interogări în timp real:
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']);
Ruta rădăcină returnează o vizualizare de bun venit, în timp ce URL-ul /search este rezervat pentru funcționalitatea de căutare . Metoda index() a controlerului afișează formularul și datele de intrare pentru căutare, în timp ce metoda search() procesează cererile asincrone trimise din browser.
În controler poți implementa un model foarte practic: pregătește un array de răspunsuri implicit în caz de eroare și suprascrie-l doar atunci când este de fapt o cerere AJAX validă și interogarea se execută fără probleme.
Controlerul ar putea avea o structură similară cu aceasta:
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);
}
}
În acest moment, aveți ciclul complet de funcționare backend: cerere AJAX primită, verificarea faptului că este într-adevăr AJAX, interogare folosind `where like` și limitarea numărului de rezultate la o cantitate rezonabilă cu `take(10)` pentru a evita supraîncărcarea bazei de date. Răspunsul este întotdeauna trimis în JSON, ceea ce simplifică foarte mult munca frontend-ului.
Vizualizare blade și preluare JavaScript pentru căutare reactivă
Cu modelul, rutele și controlerul gata, este timpul să construim partea vizibilă: un formular cu un câmp de căutare și un bloc pentru afișarea rezultatelor , plus JavaScript-ul responsabil pentru efectuarea cererilor în fundal.
Vizualizarea Blade poate fi foarte simplă, bazându-se pe tokenul CSRF injectat de Laravel pentru a valida cererile POST și pe o intrare de căutare convenabilă de utilizat:
<!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>
În acest exemplu, scriptul ascultă evenimentul keyup pe input-ul de căutare , astfel încât fiecare apăsare de tastă declanșează o cerere de preluare a datelor către calea /search. Textul curent al câmpului este trimis în format JSON, iar anteturile cheie, cum ar fi X-Requested-With, sunt incluse pentru a indica faptul că este AJAX, împreună cu token-ul CSRF pentru a ocoli protecția nativă a Laravel.
Când sosește răspunsul, acesta este transformat în JSON și o mică listă HTML cu rezultatele este generată dinamic sau este afișat un mesaj precum „Nu s-au găsit rezultate” dacă interogarea nu returnează date. Toate acestea se întâmplă fără a reîncărca pagina, într-un mod natural pentru utilizator.
Acest model poate fi rafinat în continuare cu mici detalii UX, cum ar fi adăugarea unei debounce între apăsările de taste, afișarea unui încărcător sau gestionarea erorilor de rețea pentru a preveni apariția interfeței blocate atunci când ceva eșuează.
Căutare live cu Laravel și AJAX folosind jQuery
Deși funcția fetch a câștigat teren semnificativ, jQuery AJAX rămâne foarte popular în proiectele vechi sau în echipele care îl au deja integrat. Ideea este exact aceeași: capturați ceea ce tastează utilizatorul, faceți o cerere asincronă și actualizați DOM-ul.
Un flux de lucru tipic cu jQuery în Laravel pentru o căutare live include de obicei acești pași de bază: definirea unei rute specifice, crearea unui controller dedicat, construirea vizualizării Blade cu input-ul de căutare și, în final, adăugarea codului jQuery care declanșează AJAX pe măsură ce este tastat.
Procesul funcționează astfel: când utilizatorul începe să tasteze, jQuery trimite o interogare către server cu șirul de căutare. Laravel filtrează informațiile din baza de date, returnează un obiect JSON cu rezultatele corespunzătoare, iar jQuery actualizează un container HTML de pe pagină pentru a reflecta potrivirile, totul în câteva milisecunde.
Avantajul utilizării jQuery este că simplifică foarte mult sintaxa AJAX și este foarte ușor de citit dacă aveți deja biblioteca în proiectul dvs. Cu toate acestea, introduce o dependență suplimentară care s-ar putea să nu fie necesară dacă puteți lucra cu JavaScript modern și funcționalitate nativă de fetch.
Filtrare și căutare în timp real pe frontend cu Alpine.js
Când datele care trebuie afișate sunt relativ mici (de exemplu, mai puțin de 50 de elemente ), nu este întotdeauna util să configurați un backend cu căutări complexe. În aceste cazuri, o opțiune foarte convenabilă este filtrarea directă în browser folosind Alpine.js , fără a face cereri către server în timp ce utilizatorul tastează.
Ideea este de a precalcula un șir de căutare pentru fiecare element (de exemplu, nume, descriere și categorie cu litere mici), de a-l salva într-un atribut data-search-text și de a lăsa Alpine.js să gestioneze afișarea sau ascunderea elementelor pe baza textului introdus într-un câmp de căutare.
Componenta Alpine.js poate avea o structură similară cu aceasta: 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;
},
}
În vizualizare, fiecare card sau rând de date ar avea un atribut data-search-text cu textul deja pregătit cu litere mici , astfel încât filtrul este redus la un includes() în JavaScript, foarte rapid pentru liste scurte:
<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>
În plus, puteți afișa un bloc de stare gol numai atunci când nu există rezultate pentru termenul de căutare curent , invitând utilizatorul să modifice textul sau să ștergă câmpul cu un buton care resetează pur și simplu căutarea la un șir gol.
Această abordare are avantaje clare: nu există apeluri la server în timpul căutării , interacțiunea este practic instantanee, iar logica rămâne extrem de locală și ușor de depanat. Este perfectă pentru selectori rapizi, modale de selecție a elementelor sau cataloage mici încorporate într-o pagină Laravel.
Laravel Scout: Căutare full-text cu motoare specializate
Când lucrurile devin serioase și ai nevoie de o căutare full-text rapidă, relevantă și scalabilă , calea naturală în Laravel este Laravel Scout. Scout este un strat de integrare care îți permite să conectezi cu ușurință modelele tale Eloquent cu motoare de căutare precum Algolia, Meilisearch, propria bază de date, colecții în memorie sau chiar Elasticsearch prin intermediul unor controllere externe.
Pentru a începe cu Scout, abordarea obișnuită este să creați un nou proiect Laravel sau să reutilizați unul existent , să îl lansați cu Docker (de exemplu, folosind Laravel Sail) și apoi să instalați biblioteca cu Composer. După ce ați terminat, publicați fișierul de configurare scout.php și ajustați variabilele de mediu în funcție de driverul pe care doriți să îl utilizați.
Un flux de lucru tipic ar fi instalarea Scout cu Composer, publicarea configurației sale și activarea cozii de indexare cu `SCOUT_QUEUE=true` în fișierul `.env`, astfel încât operațiunile care consumă multe resurse să fie procesate în fundal, îmbunătățind timpii de răspuns ai aplicațiilor. În plus, ar trebui să vă asigurați că `DB_HOST` indică baza de date pe care o utilizați, ceea ce este deosebit de important dacă utilizați containere Docker.
Pentru ca un model să participe la căutările Scout, acesta trebuie marcat explicit ca fiind căutabil prin adăugarea trăsăturii „Searchable” („Posibil să caute”) . De exemplu, dacă aveți un model „Train” („Tren”) care reprezintă un tabel de trenuri cu un câmp „title” („titlu”), îl puteți defini astfel:
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';
}
}
Metoda `searchableAs` vă permite să personalizați numele indexului în motorul de căutare , în loc să utilizați numele implicit derivat din model. De acolo, Scout se ocupă de sincronizarea operațiunilor de creare, actualizare și ștergere cu indexul la distanță sau local, în funcție de driverul ales.
Laravel Scout cu Algolia: Căutare SaaS extrem de rapidă
Algolia este un serviciu SaaS axat pe furnizarea de căutări foarte rapide și relevante în volume mari de date . Dispune de un panou web pentru gestionarea indexurilor, regulilor de relevanță, sinonimelor și multe altele și se integrează perfect cu Laravel prin Scout și clientul oficial PHP.
Pentru a utiliza Algolia cu Scout, va trebui să instalați clientul PHP folosind Composer, să vă înregistrați acreditările în fișierul .env (ID-ul aplicației și cheia API de administrator) și să configurați SCOUT_DRIVER=algolia pentru a-i spune lui Scout să utilizeze acest motor. Puteți obține atât ID-ul aplicației, cât și cheia de administrator din tabloul de bord Algolia.
Odată ce mediul este configurat, puteți utiliza metode precum Train::search('text')->paginate(6) direct în controllere pentru a efectua căutări pe câmpurile indexate, primind rezultate în format Eloquent paginat, gata de a fi transmise către o vizualizare Blade.
De exemplu , ați putea avea un controler de index care listează toate trenurile sau efectuează o căutare dacă este transmis un parametru titlesearch și o metodă de creare pentru a insera trenuri noi în 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();
}
În vizualizarea corespunzătoare, puteți combina un formular pentru adăugarea de trenuri noi și un alt formular GET cu un câmp de căutare a titlului care declanșează căutarea la trimitere. Apoi, pur și simplu parcurgeți colecția de trenuri și afișați câmpurile acestora într-un tabel, profitând de linkurile de paginare generate de Laravel.
Cercetați cu Meilisearch, baza de date și colecții
Dacă preferați să evitați serviciile externe, Meilisearch este un motor de căutare open-source pe care îl puteți implementa local sau pe infrastructura dvs. Scout se integrează cu Meilisearch într-un mod foarte similar cu Algolia, pur și simplu prin schimbarea driverului și adăugarea variabilelor MEILISEARCH_HOST și MEILISEARCH_KEY în fișierul .env.
Pentru a-l utiliza, instalați clientul PHP Meilisearch, setați SCOUT_DRIVER=meilisearch și indicați MEILISEARCH_HOST către URL-ul instanței (de exemplu, http://127.0.0.1:7700). Dacă aveți deja înregistrări, le puteți indexa cu comanda php artisan scout:import "App\Models\Train" astfel încât motorul să le aibă disponibile.
Pentru aplicații mai mici sau de dimensiuni moderate, puteți alege și driverul de bază de date Scout , care utilizează indexuri full-text și instrucțiuni LIKE în baza de date MySQL sau PostgreSQL. În acest caz, nu aveți nevoie de un serviciu extern; pur și simplu setați SCOUT_DRIVER=database pentru ca Scout să utilizeze baza de date însăși ca motor de căutare.
O altă opțiune interesantă este driverul de colecție, care funcționează cu colecții Eloquent în memorie . Acest motor filtrează rezultatele folosind `where` și metode de filtrare a colecțiilor și este compatibil cu orice bază de date acceptată de Laravel. Îl puteți activa cu `SCOUT_DRIVER=collection` sau ajustând fișierul de configurare Scout dacă doriți ceva mai specific.
Integrare cu Elasticsearch folosind Explorer
Dacă nevoile tale de căutare implică lucrul cu volume masive de date și analize în timp real , Elasticsearch este un clasic. În ecosistemul Laravel, o modalitate modernă de a-l integra cu Scout este utilizarea controlerului Explorer, care acționează ca o punte între modelele tale și un cluster Elasticsearch.
De obicei, aceasta implică lucrul cu Docker și un fișier docker-compose bogat care lansează, pe lângă serviciile obișnuite (Laravel, MySQL, Redis, Meilisearch etc.), containere Elasticsearch și Kibana . Apoi, instalați pachetul jeroen-g/explorer prin Composer și publicați fișierul său de configurare pentru a specifica ce modele ar trebui indexate.
În fișierul config/explorer.php, puteți înregistra modelele sub cheia indexes, de exemplu adăugând App\Models\Train::class . În plus, schimbați driverul de la Scout la Elasticsearch în fișierul .env cu SCOUT_DRIVER=elastic, astfel încât totul să indice către Elasticsearch.
În cadrul modelului Train, trebuie să implementați interfața Explored și să suprascrieți metoda mappableAs , care definește harta câmpurilor care vor fi trimise către index. Un exemplu minimal ar fi:
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,
];
}
}
De aici, puteți lansa căutări pe Elasticsearch folosind aceeași interfață Scout , beneficiind de timpi de răspuns foarte mici și de puterea completă de interogare a acestui motor, dar fără a părăsi ecosistemul Laravel.
Cu toate aceste abordări — de la completarea automată de bază cu fetch sau jQuery, la filtrarea frontend cu Alpine.js, până la căutări full-text cu Laravel Scout și diverse drivere — Laravel vă oferă o gamă largă de opțiuni pentru a implementa căutări în timp real adaptate la dimensiunea proiectului dvs., performanța de care aveți nevoie și infrastructura pe care sunteți dispus să o mențineți.