はじめに
Laravelのpaginationを利用するにはクエリビルダまたはEloquentクエリでpaginateメソッドを使用することで簡単にできます。
しかし、何重もサブクエリを定義しているSQLをクエリビルダやEloquentクエリで記述するには困難なので直接SQLを実行したい場合があると思いますが、その場合はpaginateメソッドを使用することができません。
今回は直接SQLを実行した場合でもLaravelのpaginationを利用する方法を紹介します。
※Laravelのpaginationにはいくつか種類がありますが、今回紹介するのはpaginateメソッドで利用できるものにフォーカスしています
paginationを実現するには
paginateメソッドを実行した時の返り値はIlluminate\Pagination\LengthAwarePaginator
です。
このインスタンスがpagination機能で必要なメソッドを持っているので、SQLを直接実行して取得したデータをLengthAwarePaginator
に設定することでpagination機能を実現することができます。
LengthAwarePaginatorのインタスタンスを取得する
LengthAwarePaginatorのコンストラクタには以下の引数を設定する必要があります
item: 表示するアイテム
total: アイテムの総数
perPage: 1ページの表示数
currentPage: 現在のページ番号
上記の各値をまず取得します
// 表示アイテムを取得する為のクエリ(サンプルの為、シンプルなものにしています)
$query = 'SELECT id, name FROM users ORDER BY id';
// 現在のページ番号
$currentPage = \Illuminate\Pagination\Paginator::resolveCurrentPage();
// 1ページの表示数
$perPage = 10;
// 表示するアイテムを取得
$results = \DB::select("{$query} LIMIT :per_page OFFSET :offset", [
'per_page' => $perPage,
'offset' => $currentPage * $perPage - $perPage
]);
// アイテムの総数を取得
$total = \DB::selectOne("SELECT count(*) AS cnt FROM ({$query}) AS counter");
// LengthAwarePaginatorのインタスタンスを取得
$pagination = new \Illuminate\Pagination\LengthAwarePaginator(
$results,
$total->cnt,
$perPage,
$currentPage,
);
paginationのリンクにパラメーターを追加する
LengthAwarePaginatorのインタスタンスで生成されるリンク(次のページや前のページ等)にはページ番号(page)しかパラメーターが設定されていません。
リンクに任意の値を設定したい場合はLengthAwarePaginatorのコンストラクタにオプションを設定する必要があります。
以下はページ番号(page)以外のリクエストパラメーターをリンクに設定します。
$pagination = new LengthAwarePaginator(
$results,
$total->cnt,
$perPage,
$currentPage,
[
'path' => sprintf(
'%s%s',
request()->url(),
request()->except('page')
? '?' . http_build_query(request()->except('page'))
: ''
)
]
);
最後に
今回はSQLを直接実行した場合でもLaravelのpaginationを利用する方法を紹介しました。
paginationはよく利用する機能なので同じインスタンスで利用できるようになれば開発の手間が軽減されます。
公式ドキュメントにも説明はありますが詳しくは書かれていないので、この記事が参考になれば幸いです。
https://readouble.com/laravel/9.x/ja/pagination.html#manually-creating-a-paginator