こんにちは、フロントエンドエンジニアのてりーです。
この記事ではLaravelの検索機能を「基本 → 応用 → 本格検索(Scout+Meilisearch) → リアルタイム(Livewire)」の順でまとめて解説します。
・WHERE句での検索(完全一致/部分一致/複数条件)
・日本語のあいまい検索の実装ポイント
・Laravel Scout + Meilisearchでの全文検索
・Livewireで作るリアルタイム検索UI
・実務で効くパフォーマンス最適化(インデックス/キャッシュ)
・日本語のあいまい検索の実装ポイント
・Laravel Scout + Meilisearchでの全文検索
・Livewireで作るリアルタイム検索UI
・実務で効くパフォーマンス最適化(インデックス/キャッシュ)
📚 Laravelの基礎から学びたい方へ:
👉 Laravel学習に役立つおすすめ教材
目次
1. 基本の検索:完全一致/部分一致/複数条件
まずはEloquentを使った基本パターンです。実務では部分一致と複数条件の組み合わせが最も使われます。
① 完全一致検索
$articles = Post::where('title', $search)->paginate(8);
② 部分一致(LIKE)検索
$articles = Post::where('title', 'LIKE', "%{$search}%")->paginate(8);
%
はワイルドカード。前後に付けると部分一致になります。
③ 複数フィールドを横断する検索
$articles = Post::where(function($q) use ($search) { $q->where('title', 'LIKE', "%{$search}%") ->orWhere('tag', 'LIKE', "%{$search}%") ->orWhere('body', 'LIKE', "%{$search}%"); })->orderBy('created_at', 'desc')->paginate(8);
2. 日本語の「あいまい検索」を安定させる
日本語は空白や全角半角の揺れでヒット率が落ちます。前処理で精度を底上げしましょう。
// 入力前処理:空白正規化 & トリム & 複数語対応 $keyword = preg_replace('/\s+/', ' ', trim($request->input('search', ''))); $keywords = array_filter(explode(' ', $keyword)); $query = Post::query(); foreach ($keywords as $word) { $query->where('title', 'like', "%{$word}%"); } $articles = $query->paginate(8);
・検索対象カラム(
・カテゴリ/期間/タグなどは別UIで絞り込み、検索精度と体験を両立。
title
, tag
, body
)にINDEXを貼ると高速化。・カテゴリ/期間/タグなどは別UIで絞り込み、検索精度と体験を両立。
3. Laravel Scout + Meilisearch で全文検索
ScoutはLaravel公式の全文検索抽象レイヤ、Meilisearchは軽量・爆速の検索エンジン。データ量が多いプロジェクトで威力を発揮します。
導入手順
composer require laravel/scout meilisearch/meilisearch-php php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
.env設定
SCOUT_DRIVER=meilisearch MEILISEARCH_HOST=http://127.0.0.1:7700
モデル設定 & 検索
use Laravel\Scout\Searchable; class Post extends Model { use Searchable; } // インデックスへ投入(初回) Post::query()->searchable(); // 検索 $posts = Post::search('laravel 検索機能')->get();
・インストールが簡単、軽量、更新が速い
・キーボードタイプ中の検索(typeahead)と相性が良い
・キーボードタイプ中の検索(typeahead)と相性が良い
4. Livewire で「リアルタイム検索」UI
ページ遷移なしで結果が更新されるので、ユーザー体験が抜群に良いです。Scout/Meilisearchと組み合わせれば爆速全文検索も可能。
コンポーネント
class SearchPosts extends \Livewire\Component { public string $search = ''; public function render() { $posts = Post::when($this->search, function($q){ $q->where('title', 'like', "%{$this->search}%"); })->limit(20)->get(); return view('livewire.search-posts', compact('posts')); } }
Blade(ビュー)
<div> <input type="text" class="form-control" placeholder="記事タイトルで検索" wire:model.debounce.300ms="search"> <ul class="mt-2"> @forelse($posts as $post) <li>{{ $post->title }}</li> @empty <li>一致する記事がありません</li> @endforelse </ul> </div>
5. 実装手順(フォーム/Controller/ページネーション)
① フォーム
<form class="form-inline my-2 my-lg-0 ml-2" method="GET"> <div class="form-group"> <input type="search" class="form-control mr-sm-2" name="search" value="{{ request('search') }}" placeholder="キーワードを入力" aria-label="検索"> </div> <input type="submit" value="検索" class="btn btn-info"> </form>
② Controller
$articles = Post::when($search = request('search'), function($q) use ($search) { $keyword = preg_replace('/\s+/', ' ', trim($search)); $words = array_filter(explode(' ', $keyword)); foreach ($words as $w) { $q->where('title', 'like', "%{$w}%"); } }) ->orderBy('created_at', 'desc') ->paginate(8) ->withQueryString(); // 入力値を維持
③ ページネーション
<div class="d-flex justify-content-center"> {{ $articles->links() }} </div>
6. パフォーマンス最適化
- INDEXを貼る:
ALTER TABLE posts ADD INDEX idx_posts_title (title);
- キャッシュ:同じ検索条件が続く場合は
Cache::remember()
を活用 - 大量データは Scout + Meilisearch を前提に設計すると◎
まとめ:最短で使える検索から本格全文検索まで
- 最短は LIKE + 複数条件
- 日本語は事前正規化でヒット率UP
- スケールするなら Scout + Meilisearch
- 体験向上は Livewireのリアルタイム検索
よりLaravelを深く学ぶならこちらもどうぞ👇
👉 【Laravel】マイグレーションの使い方(追加/更新)
👉 Laravel Debugbarでクエリとパフォーマンスを可視化
👉 【学習ロードマップ】React + TypeScript
質問があればコメントでどうぞ!この記事が役に立ったらSNSでシェアしてもらえると嬉しいです 🙏