Introduction
Laravel Scout est une solution simple, à base de drivers, qui ajoute la recherche plein texte aux modèles 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 databaseIndex full text MySQL / PostgreSQL La plupart des apps collectionFiltrage 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
Installation
composer require laravel/scout
Publiez la configuration :
php artisan vendor:publish --provider= "Laravel\Scout\ScoutServiceProvider"
Ajoutez le trait Laravel\Scout\Searchable aux modèles à indexer.
<? 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 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.
Vous pouvez préciser connexion et nom de queue :
'queue' => [
'connection' => 'redis' ,
'queue' => 'scout'
],
Démarrez un worker dédié :
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).
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 pour prévenir les doublons.
Prérequis par driver
Algolia
Configurez id et secret dans config/scout.php et installez le SDK.
composer require algolia/algoliasearch-client-php
.env :
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.
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 :
php artisan scout:sync-index-settings
Meilisearch
Meilisearch est un moteur open source performant. En local, via Docker Sail :
./vendor/bin/sail up -d meilisearch
Ou directement avec Docker :
docker run -it --rm \
-p 7700:7700 \
getmeili/meilisearch:latest \
meilisearch --master-key= "masterKey"
Installez le SDK :
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
.env :
SCOUT_DRIVER =meilisearch
MEILISEARCH_HOST =http://127.0.0.1:7700
MEILISEARCH_KEY =masterKey
Paramètres d’index (Meilisearch)
Avant d’utiliser where(), déclarez filterableAttributes ; pour orderBy(), sortableAttributes.
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.
public function toSearchableArray () : array
{
return [
'id' => ( int ) $this -> id ,
'name' => $this -> name ,
'price' => ( float ) $this -> price ,
];
}
Synchronisez :
php artisan scout:sync-index-settings
Typesense
Typesense prend en charge keyword, sémantique, géo et vector search.
composer require typesense/typesense-php
.env :
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.
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.
collection : filtre en PHP, fonctionne avec toutes les DB de Laravel dont SQLite. Adapté dev / tests / petits datasets.
Avec le moteur database, pas de gestion d’index manuelle : la recherche est directement sur la table.
Trait Searchable
Personnaliser toSearchableArray()
Par défaut, toArray() alimente l’index. Surchargez si nécessaire.
<? 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.
public function searchableAs () : string
{
return 'posts_index' ;
}
Stratégies pour le moteur database
Précisez la stratégie par colonne avec des attributs PHP.
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 ,
];
}
Indexation conditionnelle
Définissez shouldBeSearchable.
/**
* Détermine si ce modèle doit être indexé.
*/
public function shouldBeSearchable () : bool
{
return $this -> isPublished ();
}
shouldBeSearchable ne fonctionne pas avec le moteur database. Utilisez une clause where à la place.
Gestion des index
Cette section concerne principalement Algolia, Meilisearch, Typesense. Le moteur database n’a pas besoin de gestion d’index.
Importer des enregistrements existants
scout:import importe les enregistrements existants.
php artisan scout:import "App\Models\Post"
Import en queue :
php artisan scout:queue-import "App\Models\Post" --chunk=500
Vider l’index
php artisan scout:flush "App\Models\Post"
Pause de la synchronisation
Utilisez withoutSyncingToSearch pour désactiver temporairement la sync.
use App\Models\ Order ;
Order :: withoutSyncingToSearch ( function () {
// Les opérations à l'intérieur ne synchronisent pas
});
Ajout / suppression manuels
// Ajouter le résultat d'une requête
Order :: where ( 'price' , '>' , 100 ) -> searchable ();
// Via une relation
$user -> orders () -> searchable ();
unsearchable retire :
Order :: where ( 'price' , '>' , 100 ) -> unsearchable ();
delete sur un modèle retire automatiquement de l’index.
Recherche
search puis get :
use App\Models\ Order ;
$orders = Order :: search ( 'Star Trek' ) -> get ();
Renvoyer directement depuis une route donne du JSON.
use Illuminate\Http\ Request ;
Route :: get ( '/search' , function ( Request $request ) {
return Order :: search ( $request -> search ) -> get ();
});
raw() pour la réponse brute :
$orders = Order :: search ( 'Star Trek' ) -> raw ();
$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.
$orders = Order :: search ( 'Star Trek' ) -> simplePaginate ( 15 );
Blade :
< div class = "container" >
@foreach ($orders as $order)
{{ $order->price }}
@endforeach
</ div >
{{ $orders->links() }}
Filtres et tri
where ajoute des filtres.
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 ();
Sous Meilisearch, déclarez filterableAttributes au préalable.
query personnalise la requête Eloquent finale.
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.
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.
use Illuminate\Database\Eloquent\ Builder ;
protected function makeAllSearchableUsing ( Builder $query ) : Builder
{
return $query -> with ( 'author' );
}
makeAllSearchableUsing peut être inopérant avec des imports en queue : les relations ne survivent pas nécessairement à la sérialisation.
Soft delete
Si le modèle utilise soft delete , activez soft_delete: true dans config/scout.php.
Vous pouvez alors utiliser withTrashed / onlyTrashed.
// 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.
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 :
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 :
Pages associées
Eloquent ORM Rappels sur les modèles Eloquent.
Relations Eloquent Relations et eager loading.
Queues Scout peut mettre à jour l’index en arrière-plan via les queues.