> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Scout

> Paquete oficial que añade búsqueda de texto completo a los modelos Eloquent. Es compatible con Meilisearch, Algolia, Typesense y el motor basado en base de datos, y sincroniza los índices automáticamente mediante observers.

## Introducción

**Laravel Scout** es una solución sencilla basada en drivers para añadir búsqueda de texto completo a los [modelos Eloquent](/es/eloquent). Utiliza observers para mantener sincronizados los registros Eloquent con los índices de búsqueda.

Scout incluye un motor `database` integrado que utiliza índices full-text de MySQL/PostgreSQL y la cláusula `LIKE` para buscar directamente sobre la base de datos, sin necesidad de servicios externos. En entornos de producción grandes en los que necesitas tolerancia a erratas, faceting o búsqueda geográfica, los motores externos son de gran ayuda.

### Motores compatibles

| Motor        | Características                          | Uso                        |
| ------------ | ---------------------------------------- | -------------------------- |
| `database`   | Índices full-text de MySQL/PostgreSQL    | La mayoría de aplicaciones |
| `collection` | Filtrado en PHP (compatible con SQLite)  | Desarrollo local y tests   |
| Meilisearch  | Open source, tolerante a erratas, rápido | Producción                 |
| Algolia      | SaaS en la nube, con funciones avanzadas | Producción                 |
| Typesense    | Open source, con búsqueda vectorial      | Producción                 |

```mermaid theme={null}
flowchart LR
  APP["Aplicación Laravel<br>(modelos Eloquent)"] -->|"save / delete"| SCOUT["Laravel Scout<br>(observers)"]
  SCOUT -->|"Sincronización del índice"| ENGINE["Motor de búsqueda<br>Meilisearch / Algolia / Typesense"]
  USER["Usuario"] -->|"Model::search()"| APP
  ENGINE -->|"Resultados de búsqueda"| APP
```

## Instalación

Instala el paquete con Composer.

```shell theme={null}
composer require laravel/scout
```

Después publica el archivo de configuración con `vendor:publish`. Se generará `config/scout.php`.

```shell theme={null}
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
```

Por último, añade el trait `Laravel\Scout\Searchable` a los modelos que quieres hacer buscables. El trait registra los observers y activa la sincronización automática con el driver de búsqueda.

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Post extends Model
{
    use Searchable;
}
```

### Configuración de la cola

Si usas cualquier motor distinto de `database` o `collection`, es muy recomendable configurar un [driver de colas](/es/queues) antes de utilizar Scout. Con un worker corriendo, la sincronización del índice se ejecuta en segundo plano y la interfaz web es mucho más ágil.

Pon `queue` a `true` en `config/scout.php`.

```php theme={null}
'queue' => true,
```

También puedes indicar conexión y nombre de cola.

```php theme={null}
'queue' => [
    'connection' => 'redis',
    'queue' => 'scout'
],
```

Después arranca un worker dedicado.

```shell theme={null}
php artisan queue:work redis --queue=scout
```

### Usar jobs únicos

En aplicaciones con mucha escritura te puede interesar evitar encolar varias veces el mismo job para el mismo modelo. Registra las clases `MakeSearchableUniquely` y `RemoveFromSearchUniquely` en `config/scout.php` (normalmente en el método `boot` de un service provider).

```php theme={null}
use Laravel\Scout\Jobs\MakeSearchableUniquely;
use Laravel\Scout\Jobs\RemoveFromSearchUniquely;
use Laravel\Scout\Scout;

Scout::makeSearchableUsing(MakeSearchableUniquely::class);
Scout::removeFromSearchUsing(RemoveFromSearchUniquely::class);
```

Estos jobs se apoyan en los [locks de jobs únicos](/es/queues#unique-jobs) de Laravel para no despachar operaciones duplicadas sobre el mismo modelo.

## Requisitos por driver

### Algolia

Configura las credenciales `id` y `secret` en `config/scout.php` e instala el SDK PHP de Algolia.

```shell theme={null}
composer require algolia/algoliasearch-client-php
```

En `.env`:

```ini theme={null}
ALGOLIA_APP_ID=your-app-id
ALGOLIA_SECRET=your-secret-key
```

#### Configuración del índice

Con Algolia puedes gestionar los ajustes del índice desde `config/scout.php`.

```php theme={null}
use App\Models\User;

'algolia' => [
    'id' => env('ALGOLIA_APP_ID', ''),
    'secret' => env('ALGOLIA_SECRET', ''),
    'index-settings' => [
        User::class => [
            'searchableAttributes' => ['id', 'name', 'email'],
            'attributesForFaceting' => ['filterOnly(email)'],
        ],
    ],
],
```

Después ejecuta `scout:sync-index-settings` para aplicarlos.

```shell theme={null}
php artisan scout:sync-index-settings
```

### Meilisearch

[Meilisearch](https://www.meilisearch.com) es un motor de búsqueda open source muy rápido. En desarrollo local lo más cómodo es la imagen Docker de [Laravel Sail](https://laravel.com/docs/sail#meilisearch).

```shell theme={null}
# Con Laravel Sail
./vendor/bin/sail up -d meilisearch
```

Sin Sail puedes arrancarlo directamente con Docker.

```shell theme={null}
docker run -it --rm \
    -p 7700:7700 \
    getmeili/meilisearch:latest \
    meilisearch --master-key="masterKey"
```

Instala el SDK PHP.

```shell theme={null}
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
```

Configura el driver y el host en `.env`.

```ini theme={null}
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey
```

<Warning>
  Al actualizar Scout revisa siempre los [cambios que rompen la compatibilidad](https://github.com/meilisearch/Meilisearch/releases) del propio Meilisearch.
</Warning>

#### Configuración del índice (Meilisearch)

Con Meilisearch necesitas declarar de antemano en `filterableAttributes` las columnas por las que vas a filtrar con `where()`, y en `sortableAttributes` las que usarás con `orderBy()`.

```php theme={null}
use App\Models\User;

'meilisearch' => [
    'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
    'key' => env('MEILISEARCH_KEY', null),
    'index-settings' => [
        User::class => [
            'filterableAttributes' => ['id', 'name', 'email'],
            'sortableAttributes' => ['created_at'],
        ],
    ],
],
```

Presta atención a los tipos numéricos: Meilisearch solo puede aplicar operadores (`>`, `<`, etc.) sobre datos con el tipo correcto.

```php theme={null}
public function toSearchableArray(): array
{
    return [
        'id' => (int) $this->id,
        'name' => $this->name,
        'price' => (float) $this->price,
    ];
}
```

Después ejecuta `scout:sync-index-settings`.

```shell theme={null}
php artisan scout:sync-index-settings
```

### Typesense

[Typesense](https://typesense.org) es un motor open source rápido que soporta búsqueda por palabra clave, semántica, geo y vectorial.

```shell theme={null}
composer require typesense/typesense-php
```

Configura la conexión en `.env`.

```ini theme={null}
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost
TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http
```

Con Typesense, en `toSearchableArray` debes convertir la clave primaria a string y la fecha de creación a timestamp UNIX.

```php theme={null}
public function toSearchableArray(): array
{
    return array_merge($this->toArray(), [
        'id' => (string) $this->id,
        'created_at' => $this->created_at->timestamp,
    ]);
}
```

### Motores database y collection

Ideales si quieres añadir búsqueda sin servicios externos.

El **motor `database`** aprovecha los índices full-text y `LIKE` de MySQL/PostgreSQL. Suficiente para la mayoría de aplicaciones.

```ini theme={null}
SCOUT_DRIVER=database
```

El motor **`collection`** filtra en PHP, así que funciona con cualquier base de datos soportada por Laravel, incluida SQLite. Ideal para desarrollo, tests y conjuntos pequeños.

```ini theme={null}
SCOUT_DRIVER=collection
```

<Info>
  Con el motor `database` no necesitas gestionar índices manualmente: busca directamente contra la tabla.
</Info>

## Trait `Searchable`

### Personalizar `toSearchableArray()`

Por defecto se indexa todo lo que devuelve `toArray()`. Para personalizarlo, sobrescribe `toSearchableArray`.

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Post extends Model
{
    use Searchable;

    /**
     * Obtiene el array indexable del modelo.
     *
     * @return array<string, mixed>
     */
    public function toSearchableArray(): array
    {
        $array = $this->toArray();

        // Personaliza los datos...

        return $array;
    }
}
```

### Personalizar el nombre del índice

Por defecto se usa el nombre de la tabla (en plural). Sobrescribe `searchableAs` para cambiarlo.

```php theme={null}
public function searchableAs(): string
{
    return 'posts_index';
}
```

### Estrategias de búsqueda para el motor database

En el motor database puedes indicar, por columna, la estrategia más eficiente mediante atributos PHP.

```php theme={null}
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;

#[SearchUsingPrefix(['id', 'email'])]
#[SearchUsingFullText(['bio'])]
public function toSearchableArray(): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'bio' => $this->bio,
    ];
}
```

<Warning>
  Antes de usar `SearchUsingFullText`, asegúrate de tener un [índice full-text](/es/migrations) sobre esa columna.
</Warning>

### Hacer buscable de forma condicional

Para que un modelo solo sea buscable bajo cierta condición, define `shouldBeSearchable`.

```php theme={null}
/**
 * Decide si el modelo debe ser buscable.
 */
public function shouldBeSearchable(): bool
{
    return $this->isPublished();
}
```

<Warning>
  `shouldBeSearchable` no funciona con el motor database. Para conseguir el mismo comportamiento allí, utiliza [cláusulas where](#filtros-y-orden).
</Warning>

## Gestión del índice

<Info>
  Los comandos de esta sección son relevantes principalmente para motores externos (Algolia, Meilisearch, Typesense...). En el motor database no hace falta gestionar el índice.
</Info>

### Importar registros existentes

Al añadir Scout a un proyecto ya existente, importa los registros con `scout:import`.

```shell theme={null}
php artisan scout:import "App\Models\Post"
```

Puedes hacerlo en segundo plano mediante la cola.

```shell theme={null}
php artisan scout:queue-import "App\Models\Post" --chunk=500
```

### Vaciar el índice

Para eliminar del índice todos los registros de un modelo, usa `scout:flush`.

```shell theme={null}
php artisan scout:flush "App\Models\Post"
```

### Pausar la sincronización

Para desactivar temporalmente la sincronización durante operaciones Eloquent, usa `withoutSyncingToSearch`.

```php theme={null}
use App\Models\Order;

Order::withoutSyncingToSearch(function () {
    // Las operaciones dentro no sincronizan con el índice
});
```

### Añadir y eliminar registros manualmente

Puedes indexar colecciones a partir de una query.

```php theme={null}
// Indexa los resultados
Order::where('price', '>', 100)->searchable();

// A través de una relación
$user->orders()->searchable();
```

Para quitar registros del índice, `unsearchable`.

```php theme={null}
Order::where('price', '>', 100)->unsearchable();
```

Cuando eliminas un modelo con `delete`, también se elimina del índice automáticamente.

## Búsqueda

Con `search` haces búsquedas. Encadena `get` para obtener una colección de modelos Eloquent.

```php theme={null}
use App\Models\Order;

$orders = Order::search('Star Trek')->get();
```

Si lo devuelves desde un controlador o ruta, se serializa a JSON automáticamente.

```php theme={null}
use Illuminate\Http\Request;

Route::get('/search', function (Request $request) {
    return Order::search($request->search)->get();
});
```

Para los resultados en crudo, `raw`.

```php theme={null}
$orders = Order::search('Star Trek')->raw();
```

### Paginación

Con `paginate` obtienes resultados paginados igual que con una query Eloquent normal.

```php theme={null}
$orders = Order::search('Star Trek')->paginate();

// Cambia el tamaño de página
$orders = Order::search('Star Trek')->paginate(15);
```

En el motor database puedes usar `simplePaginate`, que es más eficiente en grandes volúmenes porque no calcula el total.

```php theme={null}
$orders = Order::search('Star Trek')->simplePaginate(15);
```

Ejemplo en Blade:

```html theme={null}
<div class="container">
    @foreach ($orders as $order)
        {{ $order->price }}
    @endforeach
</div>

{{ $orders->links() }}
```

## Filtros y orden

Con `where` añades filtros a la búsqueda.

```php theme={null}
use App\Models\Order;

// Igualdad
$orders = Order::search('Star Trek')->where('user_id', 1)->get();

// Alguno de los valores
$orders = Order::search('Star Trek')->whereIn('status', ['open', 'paid'])->get();

// Ninguno de los valores
$orders = Order::search('Star Trek')->whereNotIn('status', ['closed'])->get();
```

<Warning>
  Con Meilisearch necesitas declarar los [atributos filtrables](#configuración-del-índice-meilisearch) antes de usar `where`.
</Warning>

También puedes personalizar la query Eloquent con `query`.

```php theme={null}
use Illuminate\Database\Eloquent\Builder;

$orders = Order::search('Star Trek')
    ->query(fn (Builder $query) => $query->with('invoices'))
    ->get();
```

## Eager loading

Scout obtiene los IDs del motor y luego consulta Eloquent. Para evitar el problema N+1, indica el eager loading en `query`.

```php theme={null}
use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;

$orders = Order::search('Star Trek')
    ->query(fn (Builder $query) => $query->with(['invoices', 'user']))
    ->get();
```

Para cargar relaciones durante la importación masiva, define `makeAllSearchableUsing`.

```php theme={null}
use Illuminate\Database\Eloquent\Builder;

protected function makeAllSearchableUsing(Builder $query): Builder
{
    return $query->with('author');
}
```

<Warning>
  `makeAllSearchableUsing` puede no aplicarse durante importaciones por cola: cuando el job procesa la colección de modelos, las relaciones no se restauran.
</Warning>

## Soft delete

Si el modelo indexado usa [soft delete](/es/eloquent) y quieres poder buscar también registros eliminados, pon `soft_delete` a `true` en `config/scout.php`.

```php theme={null}
'soft_delete' => true,
```

Con esto puedes usar `withTrashed` y `onlyTrashed`.

```php theme={null}
// Incluir eliminados
$orders = Order::search('Star Trek')->withTrashed()->get();

// Solo eliminados
$orders = Order::search('Star Trek')->onlyTrashed()->get();
```

## Motores personalizados

Si ninguno de los motores integrados encaja, puedes implementar el tuyo. Debes extender la clase abstracta `Laravel\Scout\Engines\Engine` e implementar los ocho métodos siguientes.

```php theme={null}
use Laravel\Scout\Builder;

abstract public function update($models);
abstract public function delete($models);
abstract public function search(Builder $builder);
abstract public function paginate(Builder $builder, $perPage, $page);
abstract public function mapIds($results);
abstract public function map(Builder $builder, $results, $model);
abstract public function getTotalCount($results);
abstract public function flush($model);
```

Como referencia, revisa la clase `Laravel\Scout\Engines\AlgoliaEngine`.

Registra tu motor en el método `boot` de `App\Providers\AppServiceProvider`.

```php theme={null}
use App\ScoutExtensions\MySqlSearchEngine;
use Laravel\Scout\EngineManager;

public function boot(): void
{
    resolve(EngineManager::class)->extend('mysql', function () {
        return new MySqlSearchEngine;
    });
}
```

Después indícalo como driver en `config/scout.php`.

```php theme={null}
'driver' => 'mysql',
```

## Páginas relacionadas

<Card title="Eloquent ORM" icon="database" href="/es/eloquent">
  Repasa el uso básico de los modelos Eloquent.
</Card>

<Card title="Relaciones Eloquent" icon="link" href="/es/eloquent-relationships">
  Cómo definir relaciones y aplicar eager loading.
</Card>

<Card title="Colas" icon="clock" href="/es/queues">
  Scout puede actualizar el índice en segundo plano usando colas.
</Card>


## Related topics

- [Búsqueda](/es/search.md)
- [MongoDB](/es/mongodb.md)
- [Laravel Sail](/es/sail.md)
- [Actualización de Laravel — Marzo de 2026](/es/blog/changelog/202603.md)
- [Laravel Telescope](/es/telescope.md)
