Customize Searchable Data
Customize records
Scout Extended transforms your model into Algolia records with the toSearchableArray
method. You can override this method to customize what data you want to index.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Article extends Model
{
use Searchable;
/**
* Get the indexable data array for the model.
*
* @return array
*/
public function toSearchableArray()
{
$array = $this->toArray();
// Applies Scout Extended default transformations:
$array = $this->transform($array);
// Add an extra attribute:
$array['added_month'] = substr($array['created_at'], 0, 7);
return $array;
}
}
To know more about the transform
method check the section Transformers.
Relationships
Algolia doesn’t do any JOIN
operations. All searchable data must exist in each record.
This means that model relationships aren’t indexed by default. If you want to index relationships within a record, you need to override the toSearchableArray
method in your model and include the relationships you want to index.
Add attributes from relations
For example, if you are indexing articles and each article has one author, all you need to do is add its full name or email.
1
2
3
4
5
6
7
8
9
10
11
public function toSearchableArray()
{
$array = $this->toArray();
$array = $this->transform($array);
$array['author_name'] = $this->author->name;
$array['author_email'] = $this->author->email;
return $array;
}
Many-to-many
If you want to index the entire relationship you can do it by loading them before calling the toArray
method.
The resulting object has categories, converted to an array by Laravel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function toSearchableArray()
{
/**
* Load the categories relation so that it's
* available in the Laravel toArray() method
*/
$this->categories;
$array = $this->toArray();
$array = $this->transform($array);
return $array;
}
It could be that not all data from the relation. To clean up the data, you can do something like this:
1
2
3
4
5
6
7
8
9
10
11
12
public function toSearchableArray()
{
$array = $this->toArray();
$array = $this->transform($array);
$array['categories'] = $this->categories->map(function ($data) {
return $data['name'];
})->toArray();
return $array;
}
Bear in mind that Algolia has a size limit per record, so you should just index the data you need.
Updating relations when parent/child change
Once you embed information from a relation in your record, you may want to keep them up to date when you change the relation.
Depending on the relationship type between your models, you have two solutions: using the $touches
property, and listening for the saved
event.
Using the $touches
property
Laravel has a built-in feature to let the parent relationship know that one of their children has changed.
In a typical Article <=> Comment example, all you would need to do is add article
to the $touches
property.
1
2
3
4
5
6
7
8
9
10
11
class Comment extends Model
{
use Searchable;
protected $touches = ['article'];
public function article()
{
return $this->belongsTo(Article::class);
}
}
This method only works with belongsTo
and belongsToMany
relationships.
Listening to saved
event
If you use any other relationship, you have to listen to the saved
event and trigger the indexing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Author extends Model
{
public static function boot()
{
parent::boot();
static::saved(function ($model) {
$model->articles->filter(function ($item) {
return $item->shouldBeSearchable();
})->searchable();
});
}
public function articles()
{
return $this->hasMany(Article::class);
}
}
Transformers
Some builder methods such as where
and whereBetween
require numeric values. For this reason, if toSearchableArray
isn’t defined, Scout Extended transforms, by default:
- Dates into
timestamps
- Numeric strings into
integers
orfloats
.
As usual, you can overwrite this behavior by implementing the toSearchableArray
method:
1
2
3
4
5
6
7
8
9
10
11
public function toSearchableArray()
{
$array = $this->toArray();
// If you want, apply the default transformations
$array = $this->transform($array);
// Apply custom treatment
return $array;
}
Writing custom transformers
One of the primary benefits of creating a Transformer
class is the ability to type-hint any dependencies your transformer may need in its constructor. The declared dependencies are automatically resolved and injected into the transformer instance.
To write a transformer, you need to create a new class that implements Algolia\ScoutExtended\Contracts\TransformerContract
. The transform
method should transform the given $value
as needed:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
namespace App\Search\Transformers;
use App\Contracts\TransformService;
use Algolia\ScoutExtended\Contracts\TransformerContract;
class CustomTransformer implements TransformerContract
{
/**
* @var \App\Contracts\TransformService
*/
protected $service;
/**
* Creates a new instance of the class.
*
* @param \App\Contracts\TransformService $service
*
* @return void
*/
public function __construct(TransformService $service)
{
$this->service = $service;
}
/**
* Transforms the given array.
*
* @param object $searchable
* @param array $array
*
* @return array
*/
public function transform($searchable, array $array): array
{
$array = $this->service->transform($searchable->articleType, $array);
return $array;
}
}
Finally, apply a custom transform sending the $array
into the transform
method and specifying
the transformers to apply.
1
2
3
4
5
6
7
8
9
10
11
public function toSearchableArray()
{
$array = $this->toArray();
// Apply the custom transformations
$array = $this->transform($array, [
\App\Search\Transformers\CustomTransformer::class
]);
return $array;
}