Laravelで複数のテーブルにまたがる検索機能を実装する際、Eloquentのリレーションを活用すれば、クエリを簡潔に書くことができます。この記事では、Eloquentでの検索における whereHas
と join
の使い分けを中心に、検索フォームの作成から検索処理の実装方法まで解説します。
検索機能の概要と想定ケース
ここでは、以下のようなデータ構造を例に解説します。
users
テーブル:ユーザー情報posts
テーブル:ユーザーが投稿した記事(user_id
外部キーあり)
「投稿タイトル」と「ユーザー名」の両方にキーワード検索をかけるようなフォームを想定します。
検索フォームの作成
Bladeファイルに以下のようなフォームを用意します。
<form action="{{ route('search') }}" method="GET">
<input type="text" name="keyword" placeholder="検索キーワード" value="{{ request('keyword') }}">
<button type="submit">検索</button>
</form>
whereHasを使った検索の実装方法
whereHas
は、リレーション先の条件に一致する親レコードを検索する際に便利です。
use App\Models\Post;
public function search(Request $request)
{
$keyword = $request->input('keyword');
$query = Post::query();
if (!empty($keyword)) {
$query->where('title', 'like', "%{$keyword}%")
->orWhereHas('user', function ($q) use ($keyword) {
$q->where('name', 'like', "%{$keyword}%");
});
}
$results = $query->with('user')->get();
return view('search.result', compact('results'));
}
このようにすれば、「投稿タイトル」も「ユーザー名」も対象とした柔軟な検索が可能です。
joinを使った検索の実装方法
パフォーマンスを重視したい場合や、ソートをリレーション先のカラムで行いたい場合はjoin
を使うと便利です。
use Illuminate\Support\Facades\DB;
$keyword = $request->input('keyword');
$results = DB::table('posts')
->join('users', 'posts.user_id', '=', 'users.id')
->select('posts.*', 'users.name as user_name')
->where(function ($query) use ($keyword) {
$query->where('posts.title', 'like', "%{$keyword}%")
->orWhere('users.name', 'like', "%{$keyword}%");
})
->get();
join
を使えば、SQLレベルでの高速な結合検索が可能です。ただし、Eloquentのリレーションは使えないため、モデルインスタンスではなく生のデータとなります。
whereHasとjoinの使い分けのポイント
項目 | whereHas | join |
---|---|---|
リレーションの活用 | ○(Eloquentリレーション) | ×(手動で結合) |
パフォーマンス | △(やや遅い場合あり) | ◎(SQLレベルで高速) |
結果の取得形式 | Eloquentモデル | 配列またはオブジェクト |
スコープ・リレーション | 使える | 使えない |
まとめ
複数テーブルをまたぐ検索フォームの実装では、Eloquentの whereHas
を使えばモデル間の関係を活かした柔軟な検索が可能です。一方、join
は処理速度やSQLレベルの最適化に優れており、大量データを扱う場合に有効です。用途に応じて使い分けることで、実用的で高速な検索機能を構築できます。