Integrations / Frameworks / Laravel / Customize Searchable Data
Jun. 18, 2019

Customize Searchable Data

Customize records

Scout Extended will transform 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. By default, Laravel lazy loads all relationships, so unless your load them explicitly, the relationships won’t be indexed.

You need to override the toSearchableArray method in your model to index relationships.

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.

In the resulting object, you will have 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;
}

All the data from the relation may not be relevant, to clean up the collection you will instead 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 it’s recommended to only 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 type of relationship between your models, you have two solutions.

Using touch feature

Laravel has a built-in feature to let the parent relationship know that one of its 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 will 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 us where and whereBetween require numeric values. For this reason, by default, if toSearchableArray is not defined, Scout Extended transforms:

  • Dates into timestamps
  • Numeric strings into integers or floats.

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 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 will automatically be resolved and injected into the transformer instance.

Writing a transformer is simple. Create a new class that implements Algolia\ScoutExtended\Contracts\TransformerContract, and 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;
}

Did you find this page helpful?