【Laravel】複数条件でのソートとページネーションを共存させるクエリ設計術

【Laravel】複数条件でのソートとページネーションを共存させるクエリ設計術 Laravel

Laravelで検索画面や一覧表示を実装する際、「複数のソート条件」と「ページネーション」を同時に機能させたい」という場面はよくあります。しかし、条件の組み合わせやクエリの構築方法によっては、意図しない並び順やページネーションの不具合が発生しがちです。

この記事では、Eloquentやクエリビルダを用いて、複数のソート条件とページネーションを矛盾なく共存させる方法を、実例とともに紹介します。

基本の考え方:クエリの一貫性を保つ

ページネーションはクエリに対して「LIMIT」と「OFFSET」を発行するため、ソート条件が不明確だと並び順が崩れ、ページを跨いだ際にデータが重複したり抜けたりする可能性があります。

そのため、ソート条件は明示的かつ固定の順序で記述することが重要です。

単一カラムでのページネーション+ソート(基礎)

$users = User::orderBy('created_at', 'desc')
             ->paginate(20);

これは基本形で、「created_at」の降順で20件ずつ取得されます。

複数条件でのソートとページネーション

例えば、以下のようにユーザーを「ステータス(昇順)→登録日(降順)」で並べたいとします。

$users = User::orderBy('status', 'asc')
             ->orderBy('created_at', 'desc')
             ->paginate(20);

このようにorderBy()を連続で記述することで、複数の優先順位付きの並び順が確実に適用されます。

ソート条件をURLパラメータから動的に切り替える

以下のように、ユーザーの選択に応じてソート条件を変更するケースでは、request()の値をもとにクエリを動的に構築します。

$query = User::query();

$sort = request('sort');
$direction = request('direction', 'asc');

switch ($sort) {
  case 'name':
    $query->orderBy('name', $direction);
    break;
  case 'email':
    $query->orderBy('email', $direction);
    break;
  default:
    $query->orderBy('created_at', 'desc');
    break;
}

$users = $query->paginate(15)->withQueryString();

withQueryString()を使うことで、ページネーション時にもソートパラメータが保持されるようになります。

複雑な条件付きソート(NULL優先やCASE式)

より高度なソートを行うには、orderByRaw()DB::raw()を使ってSQLレベルで制御します。

例:NULLの値を最後に持ってくる

use Illuminate\Support\Facades\DB;

$users = User::orderByRaw('ISNULL(score), score DESC')
             ->paginate(20);

例:ステータスごとに優先順位を設定して並べる

$users = User::orderByRaw("
  CASE status
    WHEN 'premium' THEN 1
    WHEN 'general' THEN 2
    WHEN 'guest' THEN 3
    ELSE 4
  END
")->orderBy('created_at', 'desc')
  ->paginate(20);

このようにCASE式を用いることで、ビジネスロジックに即した独自の並び順が実現できます。

ページネーションとソートの状態をViewに保持する

Bladeテンプレートでソートリンクを表示する際は、現在のソート状態を反映しながらリンクを生成するとUXが向上します。

<a href="{{ route('users.index', ['sort' => 'name', 'direction' => 'asc']) }}">
  名前順に並び替え
</a>

さらに、ページネーションもソート状態を引き継いで表示するには、{{ $users->appends(request()->query())->links() }}とします。

まとめ

Laravelで複数条件のソートとページネーションを両立させるには、以下のポイントを押さえる必要があります。

  • orderBy()は複数指定可能で、優先順位に注意
  • withQueryString()で状態を保持
  • orderByRaw()やCASE式で柔軟なソートが可能

複雑な一覧画面や管理システムでは、こうした設計がパフォーマンスとユーザー体験の鍵を握ります。適切なクエリ設計で、見た目にも処理にも優れたアプリケーションを構築していきましょう。