laravel/scout
Laravel Scout adds driver-based full-text search to Eloquent models, automatically syncing model changes to search indexes. Supports Algolia, Meilisearch, and Typesense, with easy configuration via Laravel’s Scout integration.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require laravel/scout
For a specific driver (e.g., Algolia, Meilisearch, or Typesense), install its corresponding package:
composer require algolia/algoliasearch-client-php # Example for Algolia
Configuration: Publish the Scout config file:
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider" --tag="scout-config"
Update .env with your driver-specific credentials (e.g., ALGOLIA_APP_ID, ALGOLIA_SECRET).
Enable Scout on a Model:
Use the Searchable trait in your Eloquent model:
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
}
First Search:
$results = Post::search('laravel')->get();
Indexing: Scout automatically syncs model changes to the search index. For initial setup, run:
php artisan scout:import "App\Models\Post"
// Search for posts containing "laravel"
$posts = Post::search('laravel')->get();
// Paginate results
$posts = Post::search('laravel')->paginate(10);
// Filter results (e.g., published posts)
$posts = Post::search('laravel')
->where('published', true)
->get();
SCOUT_DRIVER=algolia
ALGOLIA_APP_ID=your_app_id
ALGOLIA_SECRET=your_secret
ALGOLIA_SEARCH=your_search_engine
public function toSearchableArray()
{
return [
'title' => $this->title,
'content' => $this->content,
'custom_ranking' => ['asc' => 'published_at'],
];
}
SCOUT_DRIVER=meilisearch
MEILI_HOST=http://localhost:7700
MEILI_MASTER_KEY=your_master_key
$posts = Post::search('laravel')
->where('category', 'tutorial')
->get();
SCOUT_DRIVER=typesense
TYPESENSE_HOST=http://localhost:8108
TYPESENSE_API_KEY=your_api_key
TYPESENSE_COLLECTION=posts
public function getScoutKey()
{
return "custom_prefix_{$this->id}";
}
// Use a closure for complex queries
$results = Post::search(function ($query) {
$query->where('title', 'like', '%laravel%')
->where('published_at', '>', now()->subYear());
})->get();
$results = Post::search('laravel')
->sortBy('published_at')
->get();
$results = Post::search('laravel')->paginate(15);
php artisan scout:import "App\Models\Post"
php artisan scout:queue "App\Models\Post"
php artisan scout:flush "App\Models\Post"
Extend the base engine for custom drivers:
use Laravel\Scout\Engines\Engine;
class CustomEngine extends Engine
{
public function search($query)
{
// Custom search logic
}
public function paginate($query, $perPage, $page)
{
// Custom pagination logic
}
public function map($results)
{
// Map raw results to Eloquent models
}
}
Register the engine in config/scout.php:
'driver' => CustomEngine::class,
Override default sync behavior:
use Laravel\Scout\Events\Searching;
class PostObserver
{
public function saving($model)
{
if (!$model->shouldBeSearchable()) {
$model->unsearchable();
}
}
public function saved($model)
{
if ($model->shouldBeSearchable()) {
$model->searchable();
}
}
}
where('price', '>', 100)) may require explicit casting in toSearchableArray.whereNotIn for numeric values (fixed in v10.23.1). Use strings or explicit casting.null values requires explicit handling:
$query->where('field', null); // May not work as expected; use raw queries if needed.
perPage and page are integers.scout:import cautiously in high-concurrency environments.$model->searchable(); // Force sync
php artisan queue:work
settings.Enable Scout logging in config/scout.php:
'debug' => env('SCOUT_DEBUG', false),
Check logs for raw driver queries during searches.
php artisan scout:dump "App\Models\Post"
if (!Typesense::collectionExists('posts')) {
Typesense::createCollection('posts');
}
For large datasets, use chunking:
Post::chunk(100, function ($posts) {
foreach ($posts as $post) {
$post->searchable();
}
});
$model->unsearchable(); // Opt out of indexing
scout:queue for Large Importsphp artisan scout:queue "App\Models\Post" --queue=scout
collection (in-memory) instead of algolia. Update .env if relying on defaults:
SCOUT_DRIVER=collection
scout_prefix by default. Disable with:
TYPESENSE_PREFIX=
MEILI_PREFIX=
Configure retry logic in config/scout.php:
'queue' => [
'retry_after' => 60, // Retry after 60 seconds
],
Override toSearchableArray for dynamic fields:
public function toSearchableArray()
{
return [
'title' => $this->title,
'content' => $this->content,
'tags' => $this->tags->pluck('name'), // Relationship data
];
}
Use afterRawSearch callback:
public function afterRawSearch($query)
{
return $query->filter(function ($hit) {
return $hit['published'] === true;
});
}
Extend the Builder for custom sort methods:
use Laravel\Scout\Builder;
class CustomBuilder extends Builder
{
public function sortByRelevance()
{
return $this->
How can I help you explore Laravel packages today?