Installation:
composer require algolia/scout-extended
Ensure algolia/algoliasearch-client-php is also installed (dependency).
Configuration: Publish the config file:
php artisan vendor:publish --provider="Algolia\ScoutExtended\ScoutExtendedServiceProvider" --tag="scout-extended-config"
Update .env with your Algolia credentials:
ALGOLIA_APP_ID=your_app_id
ALGOLIA_SECRET=your_secret_key
ALGOLIA_SEARCH=your_search_engine_name
First Use Case:
Add Scoutable and Searchable traits to your model:
use Algolia\ScoutExtended\Searchable;
class Product extends Model
{
use Scoutable, Searchable;
}
Define a toSearchableArray() method to specify searchable fields:
public function toSearchableArray()
{
return [
'id' => $this->id,
'name' => $this->name,
'price' => $this->price,
];
}
Run the indexer:
php artisan scout:import "App\Models\Product"
config/scout-extended.php:
Review settings like index_name, settings, and mappings for customization.Searchable for field transformations and Scoutable for indexing logic.scout:import, scout:flush, and scout:forget are critical for daily workflows.scout:import for initial setup or after data migrations:
php artisan scout:import "App\Models\User App\Models\Product"
save() or update() triggers via Scoutable trait:
$product->save(); // Automatically updates Algolia index
$product->searchable(); // Rebuilds searchable array
$product->save();
search() method with query builder syntax:
$results = Product::search('laptop')->get();
$results = Product::search('laptop')
->where('price', '<', 1000)
->where('category', 'electronics')
->with('reviews')
->get();
$results = Product::search('laptpo')->typoTolerance()->get();
searchable() in model events (e.g., created, updated):
class Product extends Model
{
protected static function booted()
{
static::updated(function ($product) {
$product->searchable();
});
}
}
Product::chunk(100, function ($products) {
foreach ($products as $product) {
$product->searchable();
$product->save();
}
});
toSearchableArray() for dynamic fields:
public function toSearchableArray()
{
return [
'name' => $this->name,
'formatted_price' => '$' . number_format($this->price, 2),
];
}
config/scout-extended.php:
'mappings' => [
'Product' => [
'name' => 'text',
'price' => 'number',
],
],
'settings' => [
'attributesForFaceting' => ['category', 'brand'],
'customRanking' => ['desc(price)'],
],
return ProductResource::collection($results);
$facets = Product::search('laptop')->facets(['category', 'brand'])->getFacets();
$results = Product::search('laptop')->paginate(10);
scout:flush before major deployments to avoid stale data.scout:forget to remove specific models from the index:
php artisan scout:forget App\Models\Product 123
ScoutExtended::logQuery(); // Enable logging in config
Index Name Conflicts:
dev, prod) using the same index name.getScoutKeyName() in your model or configure index_name in .env:
ALGOLIA_INDEX_NAME=products_prod
class Product extends Model
{
public function getScoutKeyName()
{
return config('scout-extended.index_name') . '_' . config('app.env');
}
}
Rate Limits:
scout:import with --chunk flag:
php artisan scout:import "App\Models\Product" --chunk=100
Stale Data:
save() calls.static::updated(function ($product) {
try {
$product->searchable();
$product->save();
} catch (\Exception $e) {
\Log::error("Algolia update failed for product {$product->id}: " . $e->getMessage());
}
});
scout:flush and re-import if needed.Field Type Mismatches:
number field).toSearchableArray() output:
public function toSearchableArray()
{
return [
'price' => (float) $this->price, // Ensure numeric fields are cast
];
}
config/scout-extended.php to enforce types:
'mappings' => [
'Product' => [
'price' => 'number',
],
],
Circular References:
toSearchableArray().public function toSearchableArray()
{
return [
'name' => $this->name,
'category' => $this->category->name, // Direct access
];
}
with() selectively:
$results = Product::search('laptop')->with(['category'])->get();
Enable Logging:
config/scout-extended.php:
'log' => [
'enabled' => true,
'path' => storage_path('logs/scout-extended.log'),
],
Algolia Debugger:
$
How can I help you explore Laravel packages today?