【Laravel】検索機能を実装する|簡単な掲示板アプリの作成
Laravelで簡単な掲示板アプリに検索機能を実装する方法を解説します。コントローラーでフォームからのリクエストを受け取り、Eloquentの`where`句と`like`を使って投稿タイトルを検索します。「部分一致」「前方一致」「後方一致」といった複数の検索条件を切り替える方法も学べます。GETリクエストを使った基本的な検索機能の実装フローが理解できます。
開発環境
- OS: Windows10
- Visual Studio Code: 1.73.0
- PHP: 8.3.11
- Laravel: 11.29.0
- laravel/breeze: 2.2
サンプルコード
/app/Http/Controllers/PostController.php
/app/Http/Controllers/PostController.php1 /** 2 * Display a listing of the resource. 3 */ 4- public function index() 5+ public function index(Request $request) 6 { 7- $posts = Post::all(); 8+ $query = Post::query(); 9+ 10+ if ($request->has('search') && $request->filled('search')) { 11+ $searchType = $request->input('search_type'); 12+ $searchKeyword = $request->input('search'); 13+ 14+ switch ($searchType) { 15+ case 'prefix': 16+ $query->where('title', 'like', $searchKeyword . '%'); 17+ break; 18+ case 'suffix': 19+ $query->where('title', 'like', '%' . $searchKeyword); 20+ break; 21+ case 'partial': 22+ $query->where('title', 'like', '%' . $searchKeyword . '%'); 23+ break; 24+ default: 25+ $query->where('title', 'like', '%' . $searchKeyword . '%'); 26+ break; 27+ } 28+ } 29+ 30+ $posts = $query->get(); 31+ 32 return view('posts.index', compact('posts')); 33 } 34 35
/resources/views/posts/index.blade.php
/resources/views/posts/index.blade.php1 @section('content') 2 <h2>掲示板一覧</h2> 3 <a href="{{ route('posts.create') }}" class="btn btn-primary mb-3">新規投稿</a> 4+ 5+ <form action="{{ route('posts.index') }}" method="GET" class="mb-4"> 6+ <div class="input-group"> 7+ <select name="search_type" class="form-select"> 8+ <option value="partial" {{ request('search_type') == 'partial' ? 'selected' : '' }}>部分一致</option> 9+ <option value="prefix" {{ request('search_type') == 'prefix' ? 'selected' : '' }}>前方一致</option> 10+ <option value="suffix" {{ request('search_type') == 'suffix' ? 'selected' : '' }}>後方一致</option> 11+ </select> 12+ <input type="text" name="search" class="form-control" placeholder="検索キーワード" value="{{ request('search') }}"> 13+ <button type="submit" class="btn btn-primary">検索</button> 14+ </div> 15+ </form> 16+ 17 @foreach ($posts as $post) 18 <div class="card mb-3"> 19 <div class="card-body"> 20
コード解説
変更点: indexメソッドでリクエスト情報を受け取る
/app/Http/Controllers/PostController.php1- public function index() 2+ public function index(Request $request)
投稿一覧を表示するindexメソッドの引数にRequest $requestを追加しました。これは、ユーザーが検索フォームに入力したキーワードや選択した検索タイプといった、「リクエスト情報」をメソッド内で受け取るために必要です。
Laravelの「DI(依存性の注入)」という仕組みにより、メソッドの引数にRequestクラスを指定しておくと、Laravelが自動的にHTTPリクエストの情報が詰まったRequestオブジェクトを渡してくれます。これにより、$requestという変数を使ってフォームからの値にアクセスできるようになります。
変更点: クエリビルダを使ってデータベース操作を準備する
/app/Http/Controllers/PostController.php1- $posts = Post::all(); 2+ $query = Post::query();
これまでPost::all()で全ての投稿データを一度に取得していましたが、これをPost::query()に変更しました。
Post::all()は、単純にテーブルの全レコードを取得する命令です。一方、Post::query()は、データベースへの問い合わせ(クエリ)を組み立てるための準備をする命令で、「クエリビルダ」と呼ばれるオブジェクトを生成します。
クエリビルダを使うことで、「もし検索キーワードがあれば、絞り込み条件を追加する」といったように、状況に応じて柔軟にSQLクエリを組み立ててから、最後にデータベースに問い合わせを実行できるようになります。
変更点: 検索キーワードが入力された場合のみ検索処理を実行する
/app/Http/Controllers/PostController.php1+ if ($request->has('search') && $request->filled('search')) { 2+ // ... 検索処理 ... 3+ }
if文を追加して、検索キーワードが送信されてきた場合のみ、検索の処理が実行されるようにしました。
$request->has('search')は、リクエスト情報の中にsearchという名前のパラメータが含まれているかを確認します。$request->filled('search')は、searchパラメータが存在し、かつその値が空文字列ではない(何かしら入力されている)ことを確認します。
&&は「かつ」を意味し、この2つの条件を両方満たした場合、つまり「検索キーワードがきちんと入力されて送信された場合」にのみ、if文の中の検索処理が実行されるようになります。
変更点: 検索タイプに応じて検索条件を動的に変更する
/app/Http/Controllers/PostController.php1+ $searchType = $request->input('search_type'); 2+ $searchKeyword = $request->input('search'); 3+ 4+ switch ($searchType) { 5+ case 'prefix': 6+ $query->where('title', 'like', $searchKeyword . '%'); 7+ break; 8+ case 'suffix': 9+ $query->where('title', 'like', '%' . $searchKeyword); 10+ break; 11+ case 'partial': 12+ $query->where('title', 'like', '%' . $searchKeyword . '%'); 13+ break; 14+ default: 15+ $query->where('title', 'like', '%' . $searchKeyword . '%'); 16+ break; 17+ }
if文の内側で、フォームから送られてきた検索タイプ(search_type)に応じて、検索条件を変える処理を追加しました。
まず$request->input()を使い、フォームのsearch_type(部分一致など)とsearch(キーワード)の値を取得します。
次にswitch文を使い、$searchTypeの値によって処理を分岐させています。
$query->where()は、クエリビルダに検索条件を追加するメソッドです。ここではtitleカラムを対象にlikeを使った「あいまい検索」を行っています。
'like'検索では、%が「0文字以上の任意の文字列」を表すワイルドカードとして機能します。case 'prefix':前方一致。$searchKeyword . '%'とすることで、「キーワードで始まる」タイトルを検索します。case 'suffix':後方一致。'%' . $searchKeywordとすることで、「キーワードで終わる」タイトルを検索します。case 'partial':部分一致。'%' . $searchKeyword . '%'とすることで、「キーワードを含む」タイトルを検索します。default: 上記のいずれにも当てはまらない場合の処理です。ここでは安全のため、デフォルトの動作として部分一致検索を行うように設定しています。
変更点: 組み立てた条件でデータベースから投稿を取得する
/app/Http/Controllers/PostController.php1+ $posts = $query->get();
if文やswitch文を使って組み立ててきた検索条件を元に、最終的にデータベースからデータを取得する処理です。
$query->get()メソッドを実行すると、それまでに追加されたwhere条件などが反映されたSQLクエリが生成・実行され、条件に一致するレコードがデータベースから取得されます。
もし検索キーワードが入力されていなければ、if文の処理は通らないためwhere句は追加されず、結果的にPost::query()->get()となり、全ての投稿を取得するのと同じ動作になります。
変更点: 検索キーワードを送信するためのフォームを追加する
/resources/views/posts/index.blade.php1+ <form action="{{ route('posts.index') }}" method="GET" class="mb-4"> 2+ <div class="input-group"> 3+ <!-- ... select, input, button ... --> 4+ </div> 5+ </form>
一覧ページ(index.blade.php)に、検索機能のためのフォームを設置しました。
<form>タグは、ユーザーが入力した情報をサーバーに送信するための要素です。action="{{ route('posts.index') }}"は、このフォームの送信先URLを指定しています。route('posts.index')は投稿一覧ページのURLを生成するため、フォームを送信すると自分自身のページ(PostControllerのindexメソッド)にデータが送られます。method="GET"は、データの送信方法を指定しています。GETメソッドを使うと、送信されたデータはURLの末尾に?search_type=partial&search=キーワードのような形式で付加されます。これにより、検索結果のURLをブックマークしたり、他の人に共有したりすることが容易になります。
変更点: 検索方法(部分一致・前方一致・後方一致)を選択できるようにする
/resources/views/posts/index.blade.php1+ <select name="search_type" class="form-select"> 2+ <option value="partial" {{ request('search_type') == 'partial' ? 'selected' : '' }}>部分一致</option> 3+ <option value="prefix" {{ request('search_type') == 'prefix' ? 'selected' : '' }}>前方一致</option> 4+ <option value="suffix" {{ request('search_type') == 'suffix' ? 'selected' : '' }}>後方一致</option> 5+ </select>
フォーム内に、検索方法を選択するためのプルダウンメニューを追加しました。
<select>タグはプルダウンメニューを作成します。name="search_type"で指定した名前が、コントローラー側で値を受け取る際のキーになります。<option>タグが各選択肢です。value属性に設定された値(partial,prefix,suffix)が、実際にサーバーに送信される値です。{{ request('search_type') == 'partial' ? 'selected' : '' }}の部分は、検索実行後もユーザーが選択した項目が選択されたままになるようにするための記述です。request('search_type')で現在のリクエストに含まれるsearch_typeの値を取得し、それがそのoptionのvalueと一致する場合にselected属性を出力して、その項目が選択された状態にしています。
変更点: 検索キーワードの入力欄と検索実行ボタンを設置する
/resources/views/posts/index.blade.php1+ <input type="text" name="search" class="form-control" placeholder="検索キーワード" value="{{ request('search') }}"> 2+ <button type="submit" class="btn btn-primary">検索</button>
検索キーワードを入力するためのテキストボックスと、検索を実行するためのボタンを追加しました。
<input type="text">は、一行のテキスト入力欄を作成します。name="search"で指定した名前が、コントローラー側で値を受け取る際のキーになります。value="{{ request('search') }}"は、プルダウンメニューと同様に、検索実行後も入力したキーワードが入力欄に保持されるようにするための記述です。<button type="submit">は、クリックされるとフォーム全体の内容をaction属性で指定されたURLに送信する役割を持ちます。
おわりに
お疲れ様でした。今回は、GETメソッドのフォームから送られた値を使って、データベースを検索する一連の流れを実装しました。コントローラー側でPost::query()を使い、if文で検索キーワードの有無を判定し、where句で絞り込み条件を追加するというのが重要なポイントです。switch文で「部分一致」や「前方一致」などの条件を切り替えることで、より実用的な検索機能が作れることも学びました。この、リクエストに応じて動的にクエリを組み立てる方法は、Webアプリケーション開発における基本となります。