> ## 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

> Package officiel qui ajoute la recherche plein texte aux modèles Eloquent. Compatible Meilisearch, Algolia, Typesense et moteur database. Synchronisation automatique via observer.

## Introduction

**Laravel Scout** est une solution simple, à base de drivers, qui ajoute la recherche plein texte aux [modèles Eloquent](/fr/eloquent). Un observer synchronise automatiquement les enregistrements avec l'index.

Scout inclut un moteur `database` intégré qui utilise l'index full text et `LIKE` de MySQL / PostgreSQL — sans service externe. Pour de grosses productions avec tolérance aux fautes, facettes ou géo-recherche, un moteur externe est utile.

### Moteurs supportés

| Moteur       | Caractéristiques                         | Usage               |
| ------------ | ---------------------------------------- | ------------------- |
| `database`   | Index full text MySQL / PostgreSQL       | La plupart des apps |
| `collection` | Filtrage en PHP (compatible SQLite)      | Dev local, tests    |
| Meilisearch  | Open source, tolérant aux fautes, rapide | Production          |
| Algolia      | SaaS cloud, fonctions avancées           | Production          |
| Typesense    | Open source, vector search               | Production          |

```mermaid theme={null}
flowchart LR
  APP["Application Laravel<br>(modèles Eloquent)"] -->|"save / delete"| SCOUT["Laravel Scout<br>(observer)"]
  SCOUT -->|"Sync d'index"| ENGINE["Moteur<br>Meilisearch / Algolia / Typesense"]
  USER["Utilisateur"] -->|"Model::search()"| APP
  ENGINE -->|"Résultats"| APP
```

## Installation

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

Publiez la configuration :

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

Ajoutez le trait `Laravel\Scout\Searchable` aux modèles à indexer.

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

namespace App\Models;

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

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

### Configuration de la queue

Avec un moteur autre que `database` / `collection`, il est vivement recommandé de configurer une [queue](/fr/queues) pour que la synchronisation d'index se fasse en arrière-plan et n'impacte pas les temps de réponse.

Dans `config/scout.php`, mettez `queue` à `true`.

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

Vous pouvez préciser connexion et nom de queue :

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

Démarrez un worker dédié :

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

### Jobs uniques

Pour éviter des jobs dupliqués sur le même enregistrement, enregistrez `MakeSearchableUniquely` / `RemoveFromSearchUniquely` (souvent dans `boot`).

```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);
```

Ils utilisent les [jobs uniques](/fr/queues#unique-jobs) pour prévenir les doublons.

## Prérequis par driver

### Algolia

Configurez `id` et `secret` dans `config/scout.php` et installez le SDK.

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

`.env` :

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

#### Paramètres d'index

Gérez les paramètres d'index dans `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)'],
        ],
    ],
],
```

Puis synchronisez :

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

### Meilisearch

[Meilisearch](https://www.meilisearch.com) est un moteur open source performant. En local, via Docker Sail :

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

Ou directement avec Docker :

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

Installez le SDK :

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

`.env` :

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

<Warning>
  À chaque upgrade Scout, consultez aussi les [breaking changes de Meilisearch](https://github.com/meilisearch/Meilisearch/releases).
</Warning>

#### Paramètres d'index (Meilisearch)

Avant d'utiliser `where()`, déclarez `filterableAttributes` ; pour `orderBy()`, `sortableAttributes`.

```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'],
        ],
    ],
],
```

Attention aux types numériques : Meilisearch ne filtre correctement que si les types sont bons.

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

Synchronisez :

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

### Typesense

[Typesense](https://typesense.org) prend en charge keyword, sémantique, géo et vector search.

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

`.env` :

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

Convertissez la clé primaire en string et `created_at` en timestamp UNIX.

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

### Moteurs `database` / `collection`

Idéal sans service externe.

**database** : full text + `LIKE` sur MySQL / PostgreSQL. Suffit dans la plupart des cas.

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

**collection** : filtre en PHP, fonctionne avec toutes les DB de Laravel dont SQLite. Adapté dev / tests / petits datasets.

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

<Info>
  Avec le moteur database, pas de gestion d'index manuelle : la recherche est directement sur la table.
</Info>

## Trait Searchable

### Personnaliser `toSearchableArray()`

Par défaut, `toArray()` alimente l'index. Surchargez si nécessaire.

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

namespace App\Models;

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

class Post extends Model
{
    use Searchable;

    /**
     * Retourne les données à indexer.
     *
     * @return array<string, mixed>
     */
    public function toSearchableArray(): array
    {
        $array = $this->toArray();

        // Personnalisation...

        return $array;
    }
}
```

### Personnaliser le nom d'index

Par défaut, le nom de table (pluriel). Surchargez `searchableAs`.

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

### Stratégies pour le moteur database

Précisez la stratégie par colonne avec des attributs 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>
  Pour `SearchUsingFullText`, ajoutez un [index full text](/fr/migrations) sur les colonnes.
</Warning>

### Indexation conditionnelle

Définissez `shouldBeSearchable`.

```php theme={null}
/**
 * Détermine si ce modèle doit être indexé.
 */
public function shouldBeSearchable(): bool
{
    return $this->isPublished();
}
```

<Warning>
  `shouldBeSearchable` ne fonctionne pas avec le moteur database. Utilisez une clause `where` à la place.
</Warning>

## Gestion des index

<Info>
  Cette section concerne principalement Algolia, Meilisearch, Typesense. Le moteur database n'a pas besoin de gestion d'index.
</Info>

### Importer des enregistrements existants

`scout:import` importe les enregistrements existants.

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

Import en queue :

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

### Vider l'index

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

### Pause de la synchronisation

Utilisez `withoutSyncingToSearch` pour désactiver temporairement la sync.

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

Order::withoutSyncingToSearch(function () {
    // Les opérations à l'intérieur ne synchronisent pas
});
```

### Ajout / suppression manuels

```php theme={null}
// Ajouter le résultat d'une requête
Order::where('price', '>', 100)->searchable();

// Via une relation
$user->orders()->searchable();
```

`unsearchable` retire :

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

`delete` sur un modèle retire automatiquement de l'index.

## Recherche

`search` puis `get` :

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

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

Renvoyer directement depuis une route donne du JSON.

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

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

`raw()` pour la réponse brute :

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

### Pagination

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

// Par page
$orders = Order::search('Star Trek')->paginate(15);
```

Sur le moteur database, `simplePaginate` est efficace pour de gros volumes.

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

Blade :

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

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

## Filtres et tri

`where` ajoute des filtres.

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

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

// Dans un tableau
$orders = Order::search('Star Trek')->whereIn('status', ['open', 'paid'])->get();

// Pas dans un tableau
$orders = Order::search('Star Trek')->whereNotIn('status', ['closed'])->get();
```

<Warning>
  Sous Meilisearch, déclarez `filterableAttributes` au préalable.
</Warning>

`query` personnalise la requête Eloquent finale.

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

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

## Eager Loading

Scout récupère les IDs puis les modèles via Eloquent. Évitez N+1 avec `with()` dans `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();
```

Pour les imports en batch, définissez `makeAllSearchableUsing`.

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

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

<Warning>
  `makeAllSearchableUsing` peut être inopérant avec des imports en queue : les relations ne survivent pas nécessairement à la sérialisation.
</Warning>

## Soft delete

Si le modèle utilise [soft delete](/fr/eloquent), activez `soft_delete: true` dans `config/scout.php`.

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

Vous pouvez alors utiliser `withTrashed` / `onlyTrashed`.

```php theme={null}
// Inclut supprimés
$orders = Order::search('Star Trek')->withTrashed()->get();

// Uniquement supprimés
$orders = Order::search('Star Trek')->onlyTrashed()->get();
```

## Moteur personnalisé

Créez votre propre moteur en étendant `Laravel\Scout\Engines\Engine` et en implémentant 8 méthodes.

```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);
```

Inspirez-vous de `Laravel\Scout\Engines\AlgoliaEngine`.

Enregistrez-le dans `boot` :

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

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

Puis dans `config/scout.php` :

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

## Pages associées

<Card title="Eloquent ORM" icon="database" href="/fr/eloquent">
  Rappels sur les modèles Eloquent.
</Card>

<Card title="Relations Eloquent" icon="link" href="/fr/eloquent-relationships">
  Relations et eager loading.
</Card>

<Card title="Queues" icon="clock" href="/fr/queues">
  Scout peut mettre à jour l'index en arrière-plan via les queues.
</Card>


## Related topics

- [Recherche](/fr/search.md)
- [MongoDB](/fr/mongodb.md)
- [Laravel Sail](/fr/sail.md)
- [Mises à jour Laravel — mars 2026](/fr/blog/changelog/202603.md)
- [Laravel Telescope](/fr/telescope.md)
