- Automatically improves the search experience.
- Reindex your data in production with zero downtime.
- Gives you a quick and simple status overview of your indexes.
- Implement site-wide search amongst multiple models with aggregators.
- A useful collection of macros, facades, front-end directives, and much more.
Note: This package adds functionalities to Laravel Scout, and for this reason, we encourage you to read the Scout documentation first. Documentation for Scout can be found on the Laravel website.
Because everyone should be able to build great search, you can use Algolia's basic Community Plan. It's free up to a certain number of records and writing operations. Search operations are not part of any quota and will not be charged in any way.
Requires:
First, install Scout Extended via the Composer package manager:
composer require algolia/scout-extended
After installing Scout Extended, you should publish the Scout configuration using the vendor:publish
Artisan command. This command will publish the config/scout.php
configuration file to your config directory:
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
Then you should configure your Algolia id
and secret
credentials in your config/scout.php
configuration file.
Finally, add the Laravel\Scout\Searchable
trait to the model you would like to make searchable. This trait will register a model observer to keep the model in sync with algolia:
<?php
namespace App;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use Searchable;
}
Performance is important. However, in order for a search to be successful, results need to be relevant to the user. Scout Extended provides an scout:optimize
Artisan command that you may use to optimize the search experience with Algolia settings.
php artisan scout:optimize
The searchable
classes are automatically detected, but feel free to specify the searchable
class to optimize:
php artisan scout:optimize "App\Article"
The Artisan command scout:optimize
will generate the settings of your searchable
class, but you may need to review those settings in config/scout-articles.php
:
<?php
return [
/*
|--------------------------------------------------------------------------
| Searchable Attributes
|--------------------------------------------------------------------------
|
| Limits the scope of a search to the attributes listed in this setting. Defining
| specific attributes as searchable is critical for relevance because it gives
| you direct control over what information the search engine should look at.
|
| Supported: Null, Array
| Example: ["name", "ordered(email)", "unordered(city)"]
|
*/
'searchableAttributes' => ['subject', 'body', 'slug'],
/*
|--------------------------------------------------------------------------
| Custom Ranking
|--------------------------------------------------------------------------
|
| Custom Ranking is about leveraging business metrics to effectively rank search
| results - it's crucial for any successful search experience. Make sure that
| only "numeric" attributes are used, such as the number of sales or views.
|
| Supported: Null, Array
| Examples: ['desc(comments_count)', 'desc(views_count)']
|
*/
'customRanking' => ['desc(sales_count)', 'desc(views_count)'],
// ...
];
Once you have verified the settings file, all you need to do is synchronize the settings with Algolia using the scout:sync
Artisan command:
php artisan scout:sync
Feel free to dig further into all Algolia settings to optimize even more your search experience: Algolia Settings.
Note: You may also edit settings using the Algolia Dashboard. But make sure you apply those settings locally running the
scout:sync
Artisan command.
In order to keep your existing search experience available while reimport your data, we recommend the usage of the scout:reimport
Artisan command.
php artisan scout:reimport
To ensure that searches performed on the index during the rebuild will not be interrupted, Scout Extended uses a temporary index to import all records before moving the temporary index to the target index.
Note: If you are using the Community Plan, please verify if you have enough number of records available in order to execute the operation.
If you are not sure about the current status of your indexes, you can always run the scout:status
Artisan command to make sure that your records and your settings are up-to-date:
php artisan scout:status
Scout Extended provides a clean way to implement site-wide search among multiple models.
To create a new aggregator, use the scout:make-aggregator
Artisan command. This command will create a new aggregator class in the app/Search
directory. Don't worry if this directory does not exist in your application since it will be created the first time you run the command.
php artisan make:aggregator News
After generating your aggregator, you should fill in the models property of the class, which will be used to identify the models that should be aggregated:
<?php
namespace App\Search;
use Algolia\ScoutExtended\Searchable\Aggregator;
class News extends Aggregator
{
/**
* The names of the models that should be aggregated.
*
* @var string[]
*/
protected $models = [
\App\Event::class,
\App\Article::class,
];
}
To register an Aggregator, use the bootSearchable
method on the aggregator you wish to register. For this, you should use the boot method of one of your service providers. In this example, we'll register the aggregator in the AppServiceProvider
:
<?php
namespace App\Providers;
use App\Search\News;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
News::bootSearchable();
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
}
An aggregator is a normal searchable class, and, as usual, you may begin searching models on the aggregator using the search
method. You may need to prepare your code to receive different models instances while searching.
$models = App\Search\News::search('Laravel')->get();
echo get_class($models[0]); // "App\Article"
echo get_class($models[1]); // "App\Event"
Scout Extended improves Laravel Scout's Builder class.
The where()
supports <
, <=
, =
, !=
, >=
, >
operators:
$models = Article::search('query')->where('views', '>', 100);
$models = Article::search('query')->where('created_at', '>=', now()->subDays(7));
$models = Article::search('query')->where('views', 100); // views = 100
The count
method returns the number of hits matched by the query.
$count = Article::search('query')->count();
The with
method gives you complete access to customize search API parameters.
$models = Article::search('query')
->with([
'hitsPerPage' => 30,
'filters' => 'attribute:value',
'typoTolerance' => false,
])->get();
The aroundLatLng
method will add geolocation parameter to the search request. You can define a point with its coordinate. This method is pure syntactic sugar, you can use the method with
to specify more location details such us aroundRadius
or aroundLatLngViaIP
.
$models = Article::search('query')
->aroundLatLng(48.8588536, 2.3125377)
->get();
For performance reasons, objects in Algolia should be 10kb or less. Large records can be split into smaller documents by splitting on a logical chunk such as paragraphs or sentences.
To split an attribute, your searchable class must implement a splitAttribute
method. This means that if you want to split the body
attribute, the method name will be splitBody
.
The most basic way of split a record is doing it directly on the searchable class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use Searchable;
/**
* Splits the given value.
*
* @param string $value
* @return mixed
*/
public function splitBody($value)
{
return explode('. ', $value);
}
}
Of course, sometimes you will need to isolate the splitting logic into a dedicated class.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Algolia\ScoutExtended\Splitters\HtmlSplitter;
class Article extends Model
{
use Searchable;
/**
* Splits the given value.
*
* @param string $value
* @return mixed
*/
public function splitBody($value)
{
return HtmlSplitter::class; // You can also return an instance instead of the class name.
}
}
One of the primary benefits of creating a Splitter
class is the ability to type-hint any dependencies your splitter may need in its constructor. The declared dependencies will automatically be resolved and injected into the splitter instance.
Writing a splitter is simple. Create a new Invokable
class, and the __invoke
method should split the given $value
as needed:
<?php
namespace App\Splitters;
use App\Contracts\SplitterService;
class CustomSplitter
{
/**
* @var \App\Contracts\SplitterService
*/
protected $service;
/**
* Creates a new instance of the class.
*
* @param \App\Contracts\SplitterService $service
*
* @return void
*/
public function __construct(SplitterService $service)
{
$this->service = $service;
}
/**
* Splits the given value.
*
* @param object $searchable
* @param mixed $value
*
* @return array
*/
public function __invoke($searchable, $value)
{
$values = $this->service->split($searchable->articleType, $value);
return $values;
}
}
Distinct functionality allows you to force the algolia to return distinct results based on one attribute defined in attributeForDistinct
. Using this attribute, you can limit the number of returned records that contain the same value in that attribute.
In order to use the distinct functionality, you should configure the attributeForDistinct
in your config/scout-{index-name}.php
configuration file:
// ...
/*
|--------------------------------------------------------------------------
| Distinct
|--------------------------------------------------------------------------
|
| Using this attribute, you can limit the number of returned records that contain the same
| value in that attribute. For example, if the distinct attribute is the series_name and
| several hits (Episodes) have the same value for series_name (Laravel From Scratch).
|
| Example: 'null', 'id', 'name'
|
*/
'distinct' => true,
'attributeForDistinct' => 'slug',
// ...
Note: If the
config/scout-{index-name}.php
file doesn't exist, it will be created when you run thescout:sync
Artisan command.
The Algolia Facade may be used to interact with Algolia's PHP client.
The client
method returns an instance of AlgoliaSearch\Client
:
use Algolia\ScoutExtended\Facades\Algolia;
$client = Algolia::client();
$apiKeys = $client->listApiKeys();
The index
method returns an instance of AlgoliaSearch\Index
:
use Algolia\ScoutExtended\Facades\Algolia;
$index = Algolia::index('contacts');
$synonym = $index->getSynonym("a-unique-identifier");
$rule = $index->getRule('a-rule-id');
The analytics
method returns an instance of AlgoliaSearch\Analytics
:
use Algolia\ScoutExtended\Facades\Algolia;
$analytics = Algolia::analytics();
$test = $analytics->getABTest(42);
Note that your Admin API key is very sensitive: it should never be shared with anyone and must remain confidential.
You may want to create a front-end implementation that will hit our servers directly, without going through your backend. And for this, you will need to create a search key:
use Algolia\ScoutExtended\Facades\Algolia;
$searchKey = Algolia::searchKey(Article::class);
The generated $searchKey
will only have rights to search on the provided searchable
class.
Scout Extended is an open-sourced software licensed under the MIT license.