【Guía Práctica Completa】Diseño de Funcionalidades de Búsqueda en Laravel — Búsqueda con LIKE, Búsqueda Full-Text, Laravel Scout, Meilisearch, Algolia, Filtros, Ordenación e Interfaces de Búsqueda Accesibles
Lo que aprenderás en este artículo (Puntos clave)
- Los fundamentos del diseño de funcionalidades de búsqueda en Laravel
- Cómo elegir entre búsqueda con
LIKE, búsqueda full-text, Laravel Scout, Meilisearch y Algolia - Implementación segura de condiciones de búsqueda, filtros, ordenación y paginación
- Conceptos sobre índices de búsqueda, sincronización, reconstrucción, colas y caché
- Diseño accesible de interfaces para resultados vacíos, errores y estados de carga
- Puntos de control para probar y operar funcionalidades de búsqueda sin romperlas en producción
Público objetivo
- Ingenieros Laravel principiantes e intermedios: quienes desean agregar búsquedas a pantallas de listado pero no saben qué enfoque elegir
- Líderes técnicos: quienes desean establecer una estrategia gradual de arquitectura de búsqueda, desde búsquedas simples hasta búsquedas full-text completas
- PM / CS / Personal operativo: quienes desean reducir consultas como “no encuentro resultados” o “la ordenación es confusa”
- Diseñadores / Especialistas en accesibilidad: quienes desean hacer más comprensibles los formularios de búsqueda, resultados, estados sin resultados y soporte para lectores de pantalla
Nivel de accesibilidad: ★★★★★
La funcionalidad de búsqueda es una vía crítica para ayudar a los usuarios a llegar a la información que necesitan. Comunica claramente el número de resultados, los filtros activos, sugerencias cuando no hay resultados, estados de carga y errores utilizando texto. Diseña usando role="status", etiquetas adecuadas, estructura de encabezados, navegación por teclado e indicadores de estado que no dependan únicamente del color.
1. Introducción: La funcionalidad de búsqueda es más que “agregar un campo de entrada”
Cuando se desarrolla una funcionalidad de búsqueda en Laravel, al principio puede parecer suficiente añadir un campo de palabras clave a una página de listado y escribir una simple consulta where('title', 'like', ...). Para paneles administrativos pequeños o listas con pocos registros, esto puede funcionar perfectamente. Sin embargo, cuando los datos crecen y los requisitos de búsqueda incluyen múltiples columnas, etiquetas, categorías, estados de publicación, rangos de fechas y ordenación, la funcionalidad de búsqueda rápidamente se vuelve compleja.
La búsqueda también es una función donde los usuarios tienen altas expectativas. Si no encuentran resultados para una consulta, pueden asumir que los datos no existen. Si el orden de los resultados parece extraño, pueden sentir que el sistema es difícil de usar. Si no hay orientación cuando no se encuentran resultados, los usuarios no sabrán qué hacer después. En otras palabras, la búsqueda requiere considerar no solo el diseño de consultas backend, sino también UI, redacción, rendimiento, accesibilidad y operación.
Este artículo explica cómo evolucionar gradualmente las funcionalidades de búsqueda en Laravel. Comenzaremos con búsquedas simples usando LIKE y avanzaremos progresivamente hacia búsquedas full-text, Laravel Scout, Meilisearch y Algolia a medida que crezcan los requisitos.
2. Elegir un enfoque de búsqueda: No sobrediseñes demasiado pronto
Existen varios enfoques para implementar búsquedas. Lo importante no es empezar con un motor extremadamente avanzado, sino elegir un método que coincida con los requisitos reales.
2.1 Sistemas pequeños pueden necesitar solo búsqueda con LIKE
Por ejemplo, si solo necesitas buscar nombres de usuario o correos electrónicos en un panel administrativo, las cláusulas where de Eloquent pueden ser suficientes.
$users = User::query()
->when($request->filled('q'), function ($query) use ($request) {
$keyword = $request->string('q')->toString();
$query->where(function ($q) use ($keyword) {
$q->where('name', 'like', "%{$keyword}%")
->orWhere('email', 'like', "%{$keyword}%");
});
})
->latest()
->paginate(20)
->withQueryString();
Este enfoque es simple, fácil de entender y barato de introducir. Sin embargo, a medida que aumenta el número de registros, el rendimiento puede degradarse y características avanzadas como clasificación por relevancia, tokenización japonesa o tolerancia a errores tipográficos se vuelven difíciles.
2.2 Uso de búsqueda full-text de base de datos
Bases de datos como MySQL, MariaDB y PostgreSQL soportan índices full-text. El query builder de Laravel proporciona whereFullText() para bases de datos compatibles.
$posts = Post::query()
->whereFullText(['title', 'body'], $request->input('q'))
->paginate(20);
La búsqueda full-text es útil cuando deseas mejorar el rendimiento sin introducir servicios externos. Sin embargo, el soporte para japonés, diccionarios, puntuación de relevancia y características operativas dependen mucho de la base de datos y su configuración.
2.3 Laravel Scout simplifica la integración con motores de búsqueda
Laravel Scout proporciona un framework para agregar búsqueda full-text a modelos Eloquent. Al añadir el trait Searchable y definir campos indexables, puedes sincronizar modelos con índices de búsqueda más fácilmente.
Scout puede funcionar con motores de base de datos, Meilisearch, Algolia y otros. Un enfoque práctico es comenzar con el motor de base de datos de Scout y migrar a Meilisearch o Algolia solo cuando sea necesario.
3. Definir requisitos antes de implementar
Organizar los requisitos antes de implementar reduce rediseños costosos más adelante. Como mínimo, aclara lo siguiente:
- ¿Qué debe ser buscable?
- ¿Qué campos se incluirán en la búsqueda?
- ¿Es suficiente la coincidencia parcial o se requiere full-text?
- ¿La ordenación debe priorizar actualidad o relevancia?
- ¿Qué filtros son necesarios?
- ¿Qué debe mostrarse cuando no haya resultados?
- ¿La búsqueda incluye datos privados o restringidos?
- ¿Deben cachearse los resultados?
- ¿Se requiere búsqueda multilingüe?
- ¿Debe soportarse tolerancia a errores o sinónimos?
Por ejemplo, la búsqueda de productos en un e-commerce y la búsqueda de usuarios en un panel interno tienen requisitos de calidad completamente distintos.
4. Construcción del formulario básico de búsqueda: Usa solicitudes GET
En general, los formularios de búsqueda deben usar el método GET. Esto permite que las condiciones de búsqueda permanezcan en la URL, haciendo que compartir enlaces, usar el botón atrás del navegador y la paginación funcionen naturalmente.
<form method="GET" action="{{ route('posts.index') }}" role="search" class="mb-6">
<label for="q" class="block font-medium">
{{ __('Búsqueda por palabra clave') }}
</label>
<div class="flex gap-2">
<input
id="q"
name="q"
type="search"
value="{{ request('q') }}"
class="border rounded px-3 py-2 w-full"
placeholder="Buscar títulos o contenido"
>
<button type="submit" class="border rounded px-4 py-2">
Buscar
</button>
</div>
</form>
Los puntos importantes incluyen usar role="search", elementos label adecuados y type="search" para identificar claramente la interfaz de búsqueda. Nunca dependas únicamente del placeholder; siempre proporciona etiquetas.
5. Mostrar el número de resultados: Indica a los usuarios dónde están
Las pantallas de resultados deben mostrar claramente el número de coincidencias. Esto es importante tanto visualmente como para lectores de pantalla.
<section aria-labelledby="search-results-title">
<h1 id="search-results-title" class="text-2xl font-semibold">
Resultados de búsqueda
</h1>
<p role="status" aria-live="polite" class="mt-2 text-sm">
@if(request('q'))
Resultados para "{{ request('q') }}": {{ number_format($posts->total()) }} elementos
@else
{{ number_format($posts->total()) }} publicaciones disponibles.
@endif
</p>
</section>
Usar role="status" facilita comunicar conteos dinámicos en interfaces interactivas.
6. Resultados vacíos: La calidad de la búsqueda se nota más cuando no se encuentra nada
Mostrar una lista vacía sin explicación es poco amigable. Los usuarios necesitan orientación sobre qué hacer después.
@if($posts->isEmpty())
<section aria-labelledby="no-results-title" class="border rounded p-4 mt-6">
<h2 id="no-results-title" class="text-lg font-semibold">
No se encontraron resultados
</h2>
<p class="mt-2">
Intenta acortar tus palabras clave o buscar con términos diferentes.
</p>
<ul class="list-disc pl-5 mt-2">
<li>Revisa errores ortográficos</li>
<li>Elimina filtros o categorías</li>
<li>Prueba términos más generales</li>
</ul>
<p class="mt-3">
<a href="{{ route('posts.index') }}" class="underline">
Ver todas las publicaciones
</a>
</p>
</section>
@endif
Las pantallas sin resultados deben sugerir acciones siguientes en lugar de culpar al usuario.
7. Filtros: Separar palabras clave de condiciones de filtrado
A medida que crecen las funcionalidades de búsqueda, se vuelve necesario filtrar por categoría, estado, fechas, precios o etiquetas. En la práctica, ayuda separar la lógica de palabras clave de la lógica de filtros.
$posts = Post::query()
->select(['id', 'title', 'slug', 'status', 'category_id', 'published_at'])
->with(['category:id,name'])
->when($request->filled('q'), function ($query) use ($request) {
$keyword = $request->string('q')->toString();
$query->where(function ($q) use ($keyword) {
$q->where('title', 'like', "%{$keyword}%")
->orWhere('body', 'like', "%{$keyword}%");
});
})
->when($request->filled('category'), function ($query) use ($request) {
$query->where('category_id', $request->integer('category'));
})
->latest()
->paginate(20)
->withQueryString();
A medida que aumentan los parámetros de búsqueda, se recomienda usar FormRequest para validarlos.
8. Ordenación: Usa listas permitidas por seguridad
La ordenación es una fuente común de vulnerabilidades SQL injection. Nunca pases valores de request directamente a orderBy().
$sort = $request->input('sort', 'latest');
$query = Post::query();
match ($sort) {
'oldest' => $query->oldest(),
'title' => $query->orderBy('title'),
default => $query->latest(),
};
9. Paginación: Mantener las condiciones de búsqueda
La paginación debe preservar las condiciones activas.
$posts = $query->paginate(20)->withQueryString();
10. Laravel Scout: Definir datos indexables en el modelo
namespace App\Models;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use Searchable;
public function toSearchableArray(): array
{
return [
'id' => (string) $this->id,
'title' => $this->title,
'body' => $this->body,
'status' => $this->status,
];
}
}
La búsqueda luego se vuelve simple:
$posts = Post::search($request->input('q'))
->query(function ($query) {
$query->where('status', 'published');
})
->paginate(20);
11. Qué considerar al introducir Scout
No indexes todo. Solo incluye datos realmente necesarios para búsqueda. Indexar información privada puede convertirse en un problema grave de seguridad.
12. Cuándo necesitas Meilisearch o Algolia
Considera motores dedicados cuando necesites:
- Ordenación natural por relevancia
- Tolerancia a errores tipográficos
- Soporte multilingüe sólido
- Filtrado rápido y búsqueda facetada
- Alto rendimiento con grandes volúmenes de datos
- Analítica y mejoras de ranking
13. Sincronización de índices
Los índices deben mantenerse sincronizados con la base de datos.
php artisan scout:import "App\Models\Post"
Reconstrucción:
php artisan scout:flush "App\Models\Post"
php artisan scout:import "App\Models\Post"
14. Búsqueda basada en permisos
Los resultados de búsqueda también requieren autorización. Nunca expongas información restringida.
15. Logs de búsqueda
Los logs de búsqueda son valiosos, pero también sensibles. Las palabras clave pueden contener información personal.
Define políticas claras de retención, acceso y eliminación.
16. Caché
Si cacheas resultados, incluye condiciones en la clave.
$key = sprintf(
'posts:search:q:%s:category:%s:page:%d',
md5($request->input('q', '')),
$request->input('category', 'all'),
$request->integer('page', 1)
);
17. Accesibilidad en interfaces de búsqueda
Antes de buscar
<label for="q">Buscar artículos</label>
<input id="q" name="q" type="search">
Durante la búsqueda
<div role="status" aria-live="polite">
Buscando...
</div>
Después de buscar
<h2>Resultados de búsqueda</h2>
<p role="status">Resultados para “Laravel”: 12 elementos</p>
18. Búsqueda en tiempo real con Livewire
<input
id="q"
type="search"
wire:model.live.debounce.300ms="q"
>
19. Testing
Prueba de búsqueda por palabra clave
public function test_posts_can_be_searched_by_keyword()
{
Post::factory()->create(['title' => 'Introducción a Laravel']);
Post::factory()->create(['title' => 'Fundamentos de PHP']);
$response = $this->get('/posts?q=Laravel');
$response->assertOk()
->assertSee('Introducción a Laravel')
->assertDontSee('Fundamentos de PHP');
}
20. Errores comunes
20.1 Usar LIKE "%keyword%" para todo
Funciona en sistemas pequeños, pero es lento a gran escala.
20.2 Pasar columnas directamente a orderBy
Peligroso. Usa listas permitidas.
20.3 Perder condiciones en paginación
Usa withQueryString().
20.4 Mala experiencia sin resultados
Sugiere acciones siguientes.
20.5 Indexar información privada
Ten extremo cuidado.
21. Checklist
Método de búsqueda
- [ ] Confirmado si
LIKEes suficiente - [ ] Organizados requisitos de full-text o Scout
- [ ] Evaluados costos operativos de Meilisearch / Algolia
Entrada y condiciones
- [ ] Formularios usan GET
- [ ] Campos tienen etiquetas adecuadas
- [ ] Condiciones validadas con FormRequest
- [ ] Ordenación usa listas permitidas
Resultados
- [ ] Conteo de resultados visible
- [ ] Condiciones activas visibles
- [ ] Estados sin resultados ofrecen sugerencias
- [ ] Paginación mantiene condiciones
Seguridad
- [ ] Datos privados no aparecen
- [ ] Información sensible excluida de índices
- [ ] Existe diseño basado en permisos
Accesibilidad
- [ ] Uso adecuado de
role="search" - [ ] Conteos usan
role="status" - [ ] Estados de carga comunicados con texto
- [ ] Los estados no dependen solo del color
22. Conclusión
La búsqueda en Laravel puede comenzar con consultas simples usando LIKE. Sin embargo, a medida que aumentan el volumen de datos, la relevancia, los filtros, los permisos y la complejidad operativa, la arquitectura se vuelve cada vez más importante.
Para sistemas pequeños, Eloquent puede ser suficiente. Para plataformas públicas o e-commerce donde la calidad de búsqueda impacta directamente la experiencia de usuario, Laravel Scout, Meilisearch o Algolia pueden aportar gran valor.
Lo más importante es construir no solo el motor de búsqueda, sino también una interfaz bien diseñada. Agrega etiquetas, muestra conteos, ofrece sugerencias cuando no haya resultados y comunica claramente estados de carga y errores. La búsqueda es una guía que ayuda a los usuarios a alcanzar sus objetivos. Con Laravel, puedes construir gradualmente funcionalidades de búsqueda rápidas, precisas y accesibles para todos.
