---
# System prepended metadata

title: Laravel8 Scout Search Example
tags: [PHP, ' Backend', ' Laravel']

---

---
tags: PHP, Laravel, Backend
disqus: HackMD
---

# Laravel8 Scout Search Example

## 效果

懶得看詳細介紹可直接跳至[簡單完整範例](#簡單完整範例)，但必須先安裝完成相關套件及申請[Algolia](https://www.algolia.com/)專案
![](https://i.imgur.com/vCVSGdw.gif)

## 介紹

`Laravel Scout`提供了一個簡單的、基於驅動程序的解決方案，用於為您的`Eloquent` 模型添加全文搜索。使用模型觀察器，Scout 會自動使您的搜索索引與您的 `Eloquent` 記錄保持同步。

目前，`Scout` 附帶`Algolia`和`MeiliSearch`驅動程序。此外，`Scout` 包含一個“集合”驅動程序，專為本地開發使用而設計，不需要任何外部依賴項或第三方服務。此外，編寫自定義驅動程序很簡單，您可以使用自己的搜索實現自由擴展 `Scout`。

## 安裝

### 安裝Laravel Scout

`composer require laravel/scout`

#### 發佈配置

`php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"`

最後，將`Laravel\Scout\Searchable`特徵添加到您希望使其可搜索的模型中。此特徵將註冊一個模型觀察者，該觀察者將自動使模型與您的搜索驅動程序保持同步：

```php=
<?php

namespace App\Models;

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

class Product extends Model
{
    use Searchable;
}
```

## 申請Algolia
到[algolia](https://www.algolia.com/)申請新的專案

![](https://i.imgur.com/4uNNxGe.jpg)

選擇香港區域
![](https://i.imgur.com/3l7Bsrt.jpg)

複製專案ID以及SECRET
![](https://i.imgur.com/8Axuuxz.jpg)




## 使用Algolia驅動

使用 Algolia 驅動程序時，您應該在`config/scout.php`中配置您的 Algolia id和secret key 。配置憑據後，您還需要通過 Composer 包管理器安裝 Algolia PHP SDK：

`composer require algolia/algoliasearch-client-php`

`.env`檔案填入剛剛複製的專案id以及key
```env=
ALGOLIA_APP_ID=
ALGOLIA_SECRET=
```

### queue

運行queue將允許 Scout 將您的模型信息同步到您的搜索索引的操作進行排隊，從而為您的應用程序的 Web 界面提供更好的響應時間。

配置隊列驅動程序後，將配置文件中的queue選項值設置config/scout.php為true：

```env=
SCOUT_QUEUE=true
```

## 配置

### 配置模型索引

每個 Eloquent 模型都與給定的搜索“索引”同步，該索引包含該模型的所有可搜索記錄。換句話說，您可以將每個索引視為一個 MySQL 表。

```php=
<?php

namespace App\Models;

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

class Product extends Model
{
    use Searchable;

    /**
     * Get the name of the index associated with the model.
     *
     * @return string
     */
    public function searchableAs()
    {
        // 在Algolia Index的名稱
        return 'productts_index';
    }
}
```

### 配置可搜索數據

默認情況下，`toArray`給定模型的整個表單將被持久化到其搜索索引。如果您想自定義與搜索索引同步的數據，您可以覆蓋`toSearchableArray`模型上的方法：

```php=
<?php

namespace App\Models;

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

class Post extends Model
{
    use Searchable;

    /**
     * Get the indexable data array for the model.
     *
     * @return array
     */
    public function toSearchableArray()
    {
        $array = $this->toArray();

        // Customize the data array...
        
        // 有一對一關聯可以這樣寫
        $array['user'] = $this->user->name;
        
        // 可以自訂義欄位
        
        return $array;
    }
}
```

### 配置模型 ID

默認情況下，Scout 將使用模型的主鍵作為存儲在搜索索引中的模型的唯一 ID/鍵。如果您需要自定義此行為，您可以覆蓋模型上的getScoutKey和getScoutKeyName方法：

```php=
<?php

namespace App\Models;

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

class User extends Model
{
    use Searchable;

    /**
     * Get the value used to index the model.
     *
     * @return mixed
     */
    public function getScoutKey()
    {
        return $this->email;
    }

    /**
     * Get the key name used to index the model.
     *
     * @return mixed
     */
    public function getScoutKeyName()
    {
        return 'email';
    }
}
```

## 本地開發

雖然您在本地開發過程中可以自由使用 `Algolia` 或 `MeiliSearch` 搜索引擎，但您可能會發現使用“`collection`”引擎更方便。`collection`引擎將對您現有數據庫中的結果使用“where”子句和收集過濾，以確定適用於您的查詢的搜索結果。使用此引擎時，沒有必要為您的可搜索模型“編制索引”，因為它們只會從您的本地數據庫中檢索。

要使用`collection`引擎，您可以簡單地將SCOUT_DRIVER環境變量的值設置為`collection`，或者直接在應用程序的scout配置文件中指定驅動程序：

```env=
SCOUT_DRIVER=collection
```

一旦您將集合驅動程序指定為首選驅動程序，您就可以開始對您的模型執行搜索查詢。使用收集引擎時，不需要搜索引擎索引，例如`Algolia` 或 `MeiliSearch` 索引所需的索引。

## 索引

### 批量導入索引

使用`scout:importArtisan` 指令，該指令將所有現有記錄導入到您的搜索索引中：

`php artisan scout:import "App\Models\Post"`

`flush`命令可用於從搜索索引中刪除模型的所有記錄：

`php artisan scout:flush "App\Models\Post"`

#### 修改導入查詢

如果您想修改用於檢索所有模型以進行批量導入的查詢，您可以makeAllSearchableUsing在模型上定義一個方法。這是在導入模型之前添加可能需要的任何急切關係加載的好地方：

```php=
/**
 * Modify the query used to retrieve models when making all of the models searchable.
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $query
 * @return \Illuminate\Database\Eloquent\Builder
 */
protected function makeAllSearchableUsing($query)
{
    return $query->with('author');
}
```

### 添加記錄

一旦你已經添加了`Laravel\Scout\Searchable`性狀的模型，所有你需要做的是`save`或`create`模型實例，它會自動被添加到您的搜索索引。如果您已將 Scout 配置為使用隊列，則此操作將由queue在後台執行：

```php=
use App\Models\Order;

$order = new Order;

// ...

$order->save();
```

## 搜尋

使用該search方法搜索模型。search 方法接受將用於搜索模型的單個字符串。然後，您應該將該get方法鏈接到搜索查詢上，以檢索與給定搜索查詢匹配的 Eloquent 模型：

```php=
use App\Models\Order;

$orders = Order::search('Star Trek')->get();
```

如果您想在將原始搜索結果轉換為 Eloquent 模型之前獲得原始搜索結果，您可以使用以下raw方法：

```php=
$orders = Order::search('Star Trek')->raw();
```

#### 自定義索引

搜索查詢通常在模型`searchableAs`方法指定的索引上執行。但是，您可以使用該`within`方法來指定應該搜索的自定義索引：

```
$orders = Order::search('Star Trek')
    ->within('tv_shows_popularity_desc')
    ->get();
```

### 分頁

除了檢索模型集合之外，您還可以使用該`paginate`方法對搜索結果進行分頁。此方法將返回一個`Illuminate\Pagination\LengthAwarePaginator`實例，就像您對[傳統的 Eloquent 查詢分頁一樣](https://laravel.com/docs/8.x/pagination)：

```php=
use App\Models\Order;

$orders = Order::search('Star Trek')->paginate();
```

您可以通過將數量作為第一個參數傳遞給`paginate`方法來指定每頁檢索多少模型：

```php=
$orders = Order::search('Star Trek')->paginate(15);
```

檢索結果後，您可以使用Blade顯示結果並呈現頁面鏈接，就像對傳統的 Eloquent 查詢進行分頁一樣：

```php=
<div class="container">
    @foreach ($orders as $order)
        {{ $order->price }}
    @endforeach
</div>

{{ $orders->links() }}
```

當然，如果您想以 JSON 形式檢索分頁結果，您可以直接從路由或控制器返回分頁器實例：

```php=
use App\Models\Order;
use Illuminate\Http\Request;

Route::get('/orders', function (Request $request) {
    return Order::search($request->input('query'))->paginate(15);
});
```

## 簡單完整範例

### .env配置
```env
SCOUT_QUEUE=true
ALGOLIA_APP_ID=YOUR APP_ID
ALGOLIA_SECRET=YOUR SECRET
```

### Product Migration

```php=
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('details');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
}
```

### Product Model

```php=
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Product extends Model
{
    use HasFactory, Searchable;

    protected $fillable = ['name', 'details'];

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

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

        return $array;
    }
}
```

### Product Controller
```php=
public function index(Request $request)
{
    if ($request->has('keyword')) {
        $products = Product::search($request->keyword)
            ->paginate(5);
    } else {
        $products = Product::paginate(5);
    }
    return view('products', compact('products'));
}
```

### 路由web.php

```php=
Route::get('/products', 'App\Http\Controllers\ProductController@index')->name('products');
```

### View `products.blade.php`

```html=
<!DOCTYPE html>
<html>
    <head>
        <title>Laravel 使用scount全文檢索範例</title>
        <!-- Latest compiled and minified CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    </head>
    <body>
        <div class="container">
            <h3 class="text-center">Laravel 使用scount全文檢索範例</h3>
            <form class="form-inline mb-5" method="GET" action="{{ route('products') }}">
                <input type="text" class="form-control" name="keyword">
                <button class="btn btn-outline-secondary" type="submit">搜尋</button>
            </form>
            <table class="table table-bordered">
                <tr>
                    <th width="80px">id</th>
                    <th>名稱</th>
                    <th>介紹</th>
                    <th>創建時間</th>
                </tr>
                @if($products->count())
                    @foreach($products as $key => $product)
                        <tr>
                            <td>{{ $product->id }}</td>
                            <td>{{ $product->name }}</td>
                            <td>{{ $product->details }}</td>
                            <td>{{ $product->created_at->format('d-m-Y') }}</td>
                        </tr>
                    @endforeach
                @endif
            </table>
            {{ $products->links() }}
        </div>
    </body>
</html>
```

### 導入索引

`php artisan scout:import "App\Models\Product"`

導入後可以發現Algolia多了相關的索引
![](https://i.imgur.com/Vq3zPw7.jpg)

run `php artisan serve`
![](https://i.imgur.com/vCVSGdw.gif)

## 相關資訊

[Laravel scout官方](https://laravel.com/docs/8.x/scout)
