Laravel ScoutでAlgolia使って全文検索〜リレーション・データベース同期編 〜

<h1>はじめに</h1>
前回までにやったことは以下の記事をご参照ください。
[Lalavel ScoutでAlgoliaを使って全文検索を実装してみた！](https://qiita.com/maip0902/items/4e8a1af752a993c48aa2)

<h1>今回やること</h1>
・Algoliaにリレーションを実装
・Algoliaとデータベースの同期をとる

<h1>前提</h1>
・scoutのインストールが完了している
booksテーブルとauthorsテーブルがあり、それぞれのモデルがすでにあるものとします。

<h1>モデルの編集</h1>
まずリレーション定義をします。ここまではいつも書くLaravelのアレです。

```App\Book.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Book extends Model{

    //リレーション定義
    public function author()
    {
        return $this->belongsTo('App\Author');
    }
    
}
```

```App\Author.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Author extends Model
{
    //リレーション定義
    public function books()
    {
        return $this->hasMany('App\Book');
    }
}
```

次に全文検索できるようにメソッドを追加します。
ここで、Algoliaのリレーションについての話になるのですが、<b>作成したインデックス同士のリレーションは定義できません!!</b>
そのため、インデックス作成時にあらかじめリレーション定義したい項目を追加しておく必要があります。リレーションのカラムが追加されるイメージです。
早速処理を記述します。

```App\Book.php
<?php

namespace App;

use Laravel\Scout\Searchable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;

class Book extends Model{

    use Searchable;
    use Notifiable;

    protected $touches = ['author'];

    public function searchableAs()
    {
        return 'books_index';
    }

    public function toSearchableArray()
    {
        $array = $this->toArray();

        $array = $this->transform($array);

        $array['author_name'] = $this->author->name;

        return $array;
    }

    public function author()
    {
        return $this->belongsTo('App\Author');
    }
    
}
```
まず
`use Laravel\Scout\Searchable;
use Illuminate\Notifications\Notifiable;`
と、
`use Searchable;
    use Notifiable;`
を追加するのを忘れないでください。
また、

```
public function searchableAs()
    {
        return 'books_index';
    }
```
によりインデックスが作成されます。

次のtoSearchableArrayメソッドで、「bookのデータにauthor_nameを付与してインデックスを作成する」という処理を行っています。以下の部分にあたります。

```
$array['author_name'] = $this->author->name;
```

次にAuthorモデルには「authorのデータに著者が書いたbookを全て一緒にインデックスとして作成する」という処理を追加します。（前半のnamespaceとsearchableAsメソッドはBookモデルと同じ)

```App\Author.php
<?php

namespace App;

use Laravel\Scout\Searchable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;

class Author extends Model
{

    use Searchable;
    use Notifiable;

    public function searchableAs()
    {
        return 'authors_index';
    }

    public function toSearchableArray()
    {
        $array = $this->toArray();

        $books = $this->books;

        for($i = 0; $i < $books->count(); $i++)
        {

            $array["book_names"][$i] = $books[$i]['name'];

        }
        

        return $array;
    }

    //リレーション定義
    public function books()
    {
        return $this->hasMany('App\Book');
    }
}
```

以下の部分で関連テーブルからデータを引っ張ってきているのですが、これがおそらく２次元配列として返ってきてるのだと思います。(連想配列ではないみたいです。間違っていたらすみません)

```
$books = $this->books;
```
そのため関連テーブルのauthorのnameを取るためにforループで取り出しました。他に何か良い方法があれば知りたいです。

```
for($i = 0; $i < $books->count(); $i++)
        {

            $array["book_names"][$i] = $books[$i]['name'];

        }
```

ここまで完了したら、Algoliaの方にデータを送ります。以下のコマンドです。

```
$php artisan scout:import "App\Author"
$php artisan scout:import "App\Book"
```

Algoliaの方を見てみましょう。

book_indexの方にはauthor_nameという項目が追加され、その中にはauthorテーブルのnameの内容が追加されているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454650/e03b8a0b-55ff-3932-17d8-f570252e4c56.png)


author_indexの方にはbook_namesという項目が追加され、その中にはbookテーブルのnameの内容が追加されているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454650/4672a117-ab48-baa6-4a0a-7beb1d47a00e.png)

<h1>データベースと同期をとる</h1>
データベースに更新があった場合、同期ができている場合はリレーションも含めて同期されます。今回の場合だと、bookテーブルが更新されると関連するauthorテーブルのbook_namesカラムがちゃんと更新されます。

同期するには以下のコマンドを叩きます。

```
$php artisan scout:sync
```
もしそんなコマンドないよ〜と怒られた場合は、

```
$composer require　algolia/scout-extended
$php artisan scout:reimport

```
を叩いてみてください。

最初にsyncコマンドを叩いた時は、`scout-インデックス名.php`というファイルがconfig以下にできます。
このあともう一度

```
$php artisan scout:sync
```

することで同期が完了です。
以下のコマンドで同期できているか確認できます。

```
$php artisan scout:status
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454650/2ba235e5-6fc7-2cdc-c91d-bcc397481861.png)

synchronizedとなっていれば大丈夫。

<h1>参考資料など</h1>
圧倒的公式ドキュメント。
https://www.algolia.com/

[【Firebase】AlgoliaでFirestoreの全文検索機能を実装してみた](https://qiita.com/katsuomi/items/b80a9646585d040aa63e)