- Laravel 讓您可以實現從使用 AJAX 的簡單搜尋引擎到使用 Laravel Scout 和 Algolia、Meilisearch 或 Elasticsearch 等外部搜尋引擎的高級全文搜尋等各種功能。
- 對於輕量級搜索,使用 Alpine.js 或原生 fetch 請求在前端進行過濾可以避免伺服器過載,並改善小型列表的使用者體驗。
- Laravel Scout 集中集成了與不同搜尋引擎的集成,使模型更容易被標記為可搜尋、管理索引以及統一啟動查詢。
- 引擎(SaaS、開源或資料庫)的選擇應基於資料量、搜尋的複雜性以及專案的效能和維護要求。
當你開始使用 Laravel 並需要一個能夠即時回應的即時搜尋引擎時,很容易在成千上萬種可能的方法中迷失方向:使用 fetch 的 AJAX、jQuery、Alpine.js、使用 Algolia 或 Meilsearch 的 Scout、前端過濾等等。好消息是,Laravel 生態系統已經提供了建立流暢快速搜尋引擎所需的幾乎所有東西,讓你不會感到不知所措。
本文將介紹如何在 Laravel 中實現不同類型的即時搜索,從經典的 AJAX 自動補全到使用 Laravel Scout 和 Algolia、Meilisearch 等搜尋引擎、資料庫本身甚至 Elasticsearch 進行全文搜尋。此外,您還將探索使用 Alpine.js 的輕量級替代方案,以便在處理小型資料集時直接在瀏覽器中進行資料過濾。
Laravel 中的即時搜尋是什麼?它的基本運作原理是什麼?
即時搜尋的核心概念是,當使用者在文字方塊中輸入內容時,系統會觸發查詢,並在無需重新載入頁面的情況下更新搜尋結果。從技術角度來看,這涉及三個關鍵元件:Laravel 後端、瀏覽器 JavaScript 以及 JSON 格式的資料交換。
一方面,Laravel 作為伺服器層,負責接收請求、解析搜尋參數(輸入的文字)、查詢資料庫並傳回結構化的回應,通常以 JSON 格式傳回。該回應可以指示成功、錯誤或未找到結果。
另一方面,JavaScript 負責監聽搜尋輸入框的使用者事件,向後端發送非同步請求(AJAX),並將傳回的資料渲染到頁面上,而無需刷新整個瀏覽器。這可以透過原生 Fetch、jQuery AJAX 或像 Alpine.js 這樣的小型響應式函式庫來實現。
借助此基本機制,您可以建立從簡單的自動完成功能(包含少量記錄)到具有相關性、分頁和過濾器的高級全文搜尋引擎,並依賴 Laravel Scout 等庫和針對搜尋優化的外部搜尋引擎。
基本即時搜尋引擎的模型、路由和控制器
在深入學習 JavaScript 之前,你需要確保 Laravel 部分組織良好:一個用於搜尋的 Eloquent 模型、清晰的路由以及一個專門用於即時處理搜尋邏輯的控制器。
第一步是建立一個 Eloquent 模型來表示你要搜尋的表格。假設有一個國家/地區表,以及一個名為Country 的非常簡單的模型,該模型不包含時間戳,並且允許批量賦值:
一個用於搜尋的最小 Eloquent 模型範例:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Pais extends Model
{
use HasFactory;
protected $guarded = [];
public $timestamps = false;
}
這表明Pais 模型位於標準的 Laravel 命名空間中,繼承自 Model 類,並且允許透過 `create()` 方法為任何字段賦值,只需將受保護的數組留空即可。透過設定 `public $timestamps = false` 停用時間戳,可以避免因表中缺少 `created_at` 和 `updated_at` 欄位而導致的問題。
下一步是定義用於處理搜尋引擎顯示和 AJAX 請求的路由。一個非常常見的方案是結合一個用於顯示視圖的 GET 路由和一個用於即時接收查詢的 POST 路由:
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BuscadorController;
Route::get('/', function () {
return view('welcome');
});
Route::get('buscador', [BuscadorController::class, 'index']);
Route::post('buscador', [BuscadorController::class, 'buscar']);
根路由傳回歡迎頁面,而/search URL 則用於搜尋功能。控制器中的 index() 方法顯示表單和搜尋輸入框,而 search() 方法處理來自瀏覽器的非同步請求。
在控制器中,您可以實現一個非常實用的模式:準備一個預設回應數組以防出錯,並且只有在實際是有效的 AJAX 請求且查詢執行無誤時才覆蓋它。
控制器可以有類似這樣的結構:
namespace App\Http\Controllers;
use App\Models\Pais;
use Illuminate\Http\Request;
class BuscadorController extends Controller
{
public function index()
{
return view('welcome');
}
public function buscar(Request $request)
{
$response = [
'success' => false,
'message' => 'Hubo un error',
];
if ($request->ajax()) {
$data = Pais::where('nombre', 'like', $request->texto.'%')
->take(10)
->get();
$response = [
'success' => true,
'message' => 'Consulta correcta',
'data' => $data,
];
}
return response()->json($response);
}
}
至此,後端流程已完整:接收 AJAX 請求、驗證其是否為 AJAX 請求、使用 `where like` 進行查詢,並透過 `take(10)` 將結果數量限制在合理範圍內,以避免資料庫過載。回應始終以 JSON 格式發送,這大大簡化了前端的工作。
Blade 視圖和 JavaScript fetch 用於響應式搜尋
模型、路由和控制器準備就緒後,就可以建立可見部分了:一個帶有搜尋欄位和用於顯示結果的區塊的表單,以及負責在後台發出請求的 JavaScript 程式碼。
Blade 視圖可以非常簡單,它依賴Laravel 注入的CSRF 令牌來驗證 POST 請求,以及一個方便使用的搜尋輸入框:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<strong><meta name="csrf-token" content="{{ csrf_token() }}"></strong>
<title>Laravel</title>
</head>
<body>
<form action="" method="post">
<input type="search" name="texto" id="buscar">
</form>
<div id="resultado"></div>
<script>
window.addEventListener('load', function () {
const buscar = document.getElementById('buscar');
const resultado = document.getElementById('resultado');
buscar.addEventListener('keyup', function () {
fetch('/buscador', {
method: 'post',
body: JSON.stringify({ texto: buscar.value }),
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.head.querySelector('[name~="csrf-token"][content]').content,
},
})
.then(response => response.json())
.then(data => {
let html = '';
if (data.success) {
html += '<ul>';
for (let i in data.data) {
html += '<li>' + data.data[i].nombre + '</li>';
}
html += '<ul>';
} else {
html += 'No existen resultados';
}
resultado.innerHTML = html;
});
});
});
</script>
</body>
</html>
在這個範例中,腳本監聽搜尋框的 keyup 事件,每次按鍵都會觸發向 /search 路徑發送 fetch 請求。欄位的目前文字以 JSON 格式發送,並包含 X-Requested-With 等關鍵頭部資訊以表明這是一個 AJAX 請求,以及用於繞過 Laravel 原生保護的 CSRF 令牌。
當回應到達時,它會轉換為 JSON 格式,並動態產生一個包含結果的簡短 HTML 列表;如果查詢沒有傳回任何數據,則會顯示類似「未找到結果」的訊息。所有這些操作都無需重新加載頁面,對用戶來說非常自然。
透過一些小的使用者體驗細節可以進一步完善這種模式,例如在按鍵之間添加防抖動、顯示載入指示器或處理網路錯誤,以防止在發生故障時介面出現凍結現象。
使用 Laravel 和 AJAX 以及 jQuery 進行即時搜索
儘管 Fetch 已經獲得了廣泛的應用,但在一些老舊專案或已經整合JQuery AJAX 的團隊中,它仍然非常受歡迎。其原理完全相同:捕獲用戶輸入的內容,發出非同步請求,然後刷新 DOM。
在 Laravel 中使用 jQuery 實現即時搜尋的典型工作流程通常包括以下基本步驟:定義特定路由、建立專用控制器、建立具有搜尋輸入的 Blade 視圖,最後添加 jQuery 程式碼以在輸入時觸發 AJAX。
其工作原理如下:當使用者開始輸入時,jQuery 會向伺服器發送包含搜尋字串的查詢。 Laravel 會過濾資料庫中的信息,返回一個包含匹配結果的 JSON 對象,然後 jQuery 會更新頁面上的 HTML 容器以反映匹配結果,所有這些操作都在幾毫秒內完成。
使用 jQuery 的優點在於它極大地簡化了 AJAX 語法,如果您已經在專案中安裝了該程式庫,程式碼將非常易於閱讀。然而,它引入了一個額外的依賴項,如果您能夠使用現代 JavaScript 和原生 fetch 功能,則可能不需要這個依賴項。
使用 Alpine.js 在前端實現即時過濾和搜尋
當要顯示的資料量相對較小(例如,少於 50 個資料)時,建立具有複雜搜尋功能的後端並不總是值得的。在這種情況下,一個非常方便的選擇是使用 Alpine.js 直接在瀏覽器中進行篩選,而無需在使用者輸入時向伺服器發送請求。
其想法是預先計算每個項目的搜尋字串(例如,名稱、描述和類別,全部小寫),將其保存在 data-search-text 屬性中,然後讓 Alpine.js根據在搜尋欄位中輸入的文字來處理項目的顯示或隱藏。
Alpine.js 元件可以有類似這樣的結構:filterItems
{
search: '',
hasResults: true,
selectedValue: '',
init() {
this.$watch('search', () => this.filterItems());
this.$nextTick(() => this.$refs.searchInput?.focus());
},
filterItems() {
const searchLower = this.search.toLowerCase().trim();
const cards = this.$el.querySelectorAll('.item-card');
let visibleCount = 0;
cards.forEach(card => {
const text = card.dataset.searchText || '';
const isVisible = searchLower === '' || text.includes(searchLower);
card.style.display = isVisible ? '' : 'none';
if (isVisible) visibleCount++;
});
this.hasResults = visibleCount > 0;
},
}
在視圖中,每張卡片或每行資料都會有一個data-search-text 屬性,其中的文字已經預先準備好(小寫),因此篩選器簡化為 JavaScript 中的 includes() 函數,對於短列表來說速度非常快:
<input type="search" x-model="search" x-ref="searchInput" placeholder="Buscar..." />
<div>
<div class="item-card" data-search-text="formulario contacto simple">
<h3>Formulario de contacto</h3>
<p>Formulario de contacto simple</p>
</div>
</div>
此外,您還可以僅在當前搜尋字詞沒有結果時顯示一個空的狀態區塊,邀請使用者修改文字或透過一個按鈕清除該字段,該按鈕會將搜尋重設為空字串。
這種方法優勢明顯:搜尋過程中無需伺服器調用,互動幾乎是瞬時的,而且邏輯高度本地化,易於調試。它非常適合快速選擇器、專案選擇模態框或嵌入 Laravel 頁面中的小型目錄。
Laravel Scout:使用專用引擎進行全文搜索
當需要快速、相關且可擴展的全文搜尋時,Laravel Scout 是 Laravel 中的理想選擇。 Scout 是一個整合層,它允許你輕鬆地將 Eloquent 模型與 Algolia、Meilisearch 等搜尋引擎、你自己的資料庫、記憶體集合,甚至透過外部控制器與 Elasticsearch 連接起來。
要開始使用 Scout,通常的做法是建立一個新的 Laravel 專案或重複使用一個現有的項目,使用 Docker 啟動它(例如,使用 Laravel Sail),然後使用 Composer 安裝程式庫。完成這些步驟後,發布 scout.php 設定文件,並根據要使用的驅動程式調整環境變數。
典型的工作流程是使用 Composer 安裝 Scout,發布其配置,並在 `.env` 檔案中啟用索引佇列(`SCOUT_QUEUE=true`),以便在背景處理資源密集型操作,從而提升應用程式的回應速度。此外,您還應確保 `DB_HOST` 指向您正在使用的資料庫,如果您使用的是 Docker 容器,這一點尤其重要。
要使模型能夠參與 Scout 搜索,必須透過新增 `Searchable` 特性將其明確標記為可搜尋。例如,如果您有一個名為 `Train` 的模型,它表示一個包含 `title` 欄位的列車表,您可以這樣定義它:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Train extends Model
{
use Searchable;
protected $fillable = ['title'];
public function searchableAs()
{
return 'trains_index';
}
}
`searchableAs` 方法可讓您自訂搜尋引擎中的索引名稱,而不是使用從模型派生的預設名稱。之後,Scout 會根據所選驅動程序,處理與遠端或本機索引的建立、更新和刪除操作的同步。
Laravel Scout 與 Algolia:閃電般快速的 SaaS 搜索
Algolia 是一款 SaaS 服務,專注於在大數據集上提供快速且相關的搜尋。它具有一個用於管理索引、相關性規則、同義詞等的 Web 面板,並透過 Scout 和官方 PHP 用戶端與 Laravel 無縫整合。
要在 Scout 中使用 Algolia,您需要使用 Composer 安裝其 PHP 用戶端,在 .env 檔案中註冊您的憑證(應用程式 ID 和管理員 API 金鑰),並設定 SCOUT_DRIVER=algolia以告知 Scout 使用此引擎。您可以從 Algolia 控制面板取得應用程式 ID 和管理員金鑰。
環境設定完成後,您可以在控制器中直接使用Train::search('text')->paginate(6)等方法對索引欄位執行搜索,以分頁 Eloquent 格式接收結果,以便將其傳遞給 Blade 視圖。
例如,您可以建立索引控制器,用於列出所有列車,或在傳遞 titlesearch 參數時執行搜索,以及一個 create 方法,用於將新列車插入索引:
public function index(Request $request)
{
if ($request->has('titlesearch')) {
$trains = Train::search($request->titlesearch)->paginate(6);
} else {
$trains = Train::paginate(6);
}
return view('Train-search', compact('trains'));
}
public function create(Request $request)
{
$this->validate($request, ['title' => 'required']);
Train::create($request->all());
return back();
}
在相應的視圖中,您可以將一個用於新增列車的表單與另一個帶有標題搜尋欄位的 GET 表單組合在一起,該資料會在提交時觸發搜尋。然後,您只需遍歷列車集合,並利用 Laravel 生成的分頁鏈接,將它們的欄位顯示在表格中。
利用 Meilsearch、資料庫和館藏進行偵察
如果您不想使用外部服務,Meilisearch 是一個開源搜尋引擎,您可以將其部署在本地或您的基礎架構上。 Scout 與 Meilsearch 的整合方式與 Algolia 非常相似,只需更改驅動程式並將 MEILISEARCH_HOST 和 MEILISEARCH_KEY 變數添加到 .env 檔案中即可。
要使用它,請安裝 MeILsearch PHP 用戶端,設定SCOUT_DRIVER=meilisearch,並將 MEILISEARCH_HOST 指向實例 URL(例如,http://127.0.0.1:7700)。如果您已有記錄,可以使用指令 `php artisan scout:import "App\Models\Train"` 對其進行索引,以便引擎可以存取這些記錄。
對於規模較小或中等的應用程序,您還可以選擇Scout 的資料庫驅動程序,它利用 MySQL 或 PostgreSQL 資料庫上的全文索引和 LIKE 語句。在這種情況下,您無需外部服務;只需設定 SCOUT_DRIVER=database,Scout 即可使用資料庫本身作為其搜尋引擎。
另一個值得關注的選項是集合驅動程序,它支援記憶體中的 Eloquent 集合。引擎使用 `where` 子句和集合過濾方法來篩選結果,並且相容於 Laravel 支援的任何資料庫。您可以使用 `SCOUT_DRIVER=collection` 來啟動它,或者如果您需要更具體的配置,您也可以調整 Scout 設定檔。
使用 Explorer 與 Elasticsearch 集成
如果您的搜尋需求涉及處理大量資料和即時分析,Elasticsearch 是一個經典之選。在 Laravel 生態系統中,將 Elasticsearch 與 Scout 整合的一種現代方法是使用 Explorer 控制器,它充當模型和 Elasticsearch 叢集之間的橋樑。
這通常涉及使用 Docker 和一個功能豐富的 docker-compose 文件,該文件除了啟動常用服務(Laravel、MySQL、Redis、Meilisearch 等)之外,還會啟動Elasticsearch 和 Kibana 容器。然後,您需要透過 Composer 安裝 jeroen-g/explorer 套件,並發布其設定檔以指定要索引的模型。
在 config/explorer.php 檔案中,您可以透過新增App\Models\Train::class等方式,在 indexes 鍵下註冊您的模型。此外,您需要在 .env 檔案中將驅動程式從 Scout 變更為 Elasticsearch,方法是新增 SCOUT_DRIVER=elastic,以便所有內容都指向 Elasticsearch。
在 Train 模型中,您需要實作 Explored 介面並重寫mappableAs方法,該方法定義了要傳送至索引的欄位對應。一個簡單的範例如下:
use JeroenG\Explorer\Application\Explored;
use Laravel\Scout\Searchable;
class Train extends Model implements Explored
{
use Searchable;
protected $fillable = ['title'];
public function mappableAs(): array
{
return [
'id' => $this->id,
'title' => $this->title,
];
}
}
從這裡,您可以使用相同的 Scout 介面在 Elasticsearch 上發起搜索,享受極低的回應時間和該引擎的全部查詢能力,而無需離開 Laravel 生態系統。
從使用 fetch 或 jQuery 實作基本上自動完成,到使用 Alpine.js 進行前端過濾,再到使用 Laravel Scout 和各種驅動程式進行全文搜索,Laravel 提供了多種選擇,可以根據專案規模、所需效能和願意維護的基礎架構來實現即時搜尋。