--- 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)