データベース: ページネーション¶
- イントロダクション
- 基本的な使用法
- ページネーション結果の表示
- ページネーションビューのカスタマイズ
- ページネータとLengthAwarePaginatorインスタンスメソッド
- カーソルページネータインスタンスメソッド
イントロダクション¶
他のフレームワークでは、ページネーションは非常に苦痛なものになることがあります。Laravelのページネーションは、新鮮な息吹を感じてもらえることを願っています。Laravelのページネータは、クエリビルダとEloquent ORMと統合されており、ゼロコンフィグでデータベースレコードのページネーションを簡単に行えます。
デフォルトでは、ページネータによって生成されるHTMLは、Tailwind CSSフレームワークと互換性があります。ただし、Bootstrapのページネーションサポートも利用できます。
Tailwind JIT¶
LaravelのデフォルトのTailwindページネーションビューを使用していて、Tailwind JITエンジンを使用している場合は、アプリケーションのtailwind.config.js
ファイルのcontent
キーがLaravelのページネーションビューを参照していることを確認してください。これにより、それらのTailwindクラスが削除されないようになります。
content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
],
基本的な使用法¶
クエリビルダの結果をページネーションする¶
アイテムをページネーションする方法はいくつかあります。最も簡単な方法は、クエリビルダまたはEloquentクエリでpaginate
メソッドを使用することです。paginate
メソッドは、ユーザーが現在閲覧しているページに基づいて、クエリの「limit」と「offset」を自動的に設定します。デフォルトでは、現在のページはHTTPリクエストのpage
クエリ文字列引数の値によって検出されます。この値はLaravelによって自動的に検出され、ページネータによって生成されるリンクにも自動的に挿入されます。
この例では、paginate
メソッドに渡される唯一の引数は、「ページごと」に表示したいアイテムの数です。この場合、15
アイテムをページごとに表示するように指定しましょう。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* すべてのアプリケーションユーザーを表示します。
*/
public function index(): View
{
return view('user.index', [
'users' => DB::table('users')->paginate(15)
]);
}
}
シンプルページネーション¶
paginate
メソッドは、データベースからレコードを取得する前に、クエリに一致するレコードの総数をカウントします。これは、ページネータがレコードの総ページ数を知るために行われます。ただし、アプリケーションのUIに総ページ数を表示する予定がない場合、レコード数のクエリは不要です。
したがって、アプリケーションのUIにシンプルな「次へ」と「前へ」のリンクのみを表示する必要がある場合は、simplePaginate
メソッドを使用して、単一の効率的なクエリを実行できます。
$users = DB::table('users')->simplePaginate(15);
Eloquentの結果をページネーションする¶
Eloquentクエリをページネーションすることもできます。この例では、App\Models\User
モデルをページネーションし、15レコードをページごとに表示することを示します。クエリビルダの結果をページネーションする場合とほぼ同じ構文であることがわかります。
use App\Models\User;
$users = User::paginate(15);
もちろん、クエリに他の制約(where
句など)を設定した後にpaginate
メソッドを呼び出すこともできます。
$users = User::where('votes', '>', 100)->paginate(15);
Eloquentモデルをページネーションする場合にも、simplePaginate
メソッドを使用できます。
$users = User::where('votes', '>', 100)->simplePaginate(15);
同様に、EloquentモデルをカーソルページネーションするためにcursorPaginate
メソッドを使用できます。
$users = User::where('votes', '>', 100)->cursorPaginate(15);
1ページに複数のページネータインスタンス¶
アプリケーションによってレンダリングされる単一の画面に2つの独立したページネータをレンダリングする必要がある場合があります。ただし、両方のページネータインスタンスが現在のページを格納するためにpage
クエリ文字列パラメータを使用している場合、2つのページネータは競合します。この競合を解決するには、paginate
、simplePaginate
、およびcursorPaginate
メソッドに提供される3番目の引数に、ページネータの現在のページを格納するために使用するクエリ文字列パラメータの名前を渡します。
use App\Models\User;
$users = User::where('votes', '>', 100)->paginate(
$perPage = 15, $columns = ['*'], $pageName = 'users'
);
カーソルページネーション¶
paginate
とsimplePaginate
はSQLの「offset」句を使用してクエリを作成しますが、カーソルページネーションはクエリに含まれる順序付けられた列の値を比較する「where」句を構築することで機能し、Laravelのすべてのページネーション方法の中で最も優れたデータベースパフォーマンスを提供します。このページネーション方法は、特に大規模なデータセットと「無限」スクロールユーザーインターフェースに適しています。
オフセットベースのページネーションとは異なり、カーソルベースのページネーションはページネータによって生成されるURLのクエリ文字列にページ番号を含めません。代わりに、クエリ文字列に「カーソル」文字列を配置します。カーソルは、次のページネーションクエリがページネーションを開始する場所と方向を含むエンコードされた文字列です。
クエリビルダによって提供されるcursorPaginate
メソッドを介して、カーソルベースのページネータインスタンスを作成できます。このメソッドは、Illuminate\Pagination\CursorPaginator
のインスタンスを返します。
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
カーソルページネータインスタンスを取得したら、paginate
およびsimplePaginate
メソッドを使用する場合と同様に、ページネーション結果を表示できます。カーソルページネータによって提供されるインスタンスメソッドの詳細については、カーソルページネータインスタンスメソッドのドキュメントを参照してください。
Warning
カーソルページネーションを利用するには、クエリに「order by」句が含まれている必要があります。さらに、クエリが順序付けられる列は、ページネーションするテーブルに属している必要があります。
カーソル vs. オフセットページネーション¶
オフセットページネーションとカーソルページネーションの違いを説明するために、いくつかの例のSQLクエリを見てみましょう。以下の両方のクエリは、id
で順序付けられたusers
テーブルの「2ページ目」の結果を表示します。
# オフセットページネーション...
select * from users order by id asc limit 15 offset 15;
# カーソルページネーション...
select * from users where id > 15 order by id asc limit 15;
カーソルページネーションクエリは、オフセットページネーションに比べて以下の利点があります。
- 大規模なデータセットの場合、カーソルページネーションは「order by」列がインデックス付けされている場合により優れたパフォーマンスを提供します。これは、「offset」句が以前に一致したすべてのデータをスキャンするためです。
- 頻繁に書き込まれるデータセットの場合、オフセットページネーションは、結果が最近ユーザーが現在閲覧しているページに追加または削除された場合、レコードをスキップしたり重複したりする可能性があります。
ただし、カーソルページネーションには以下の制限があります。
simplePaginate
と同様に、カーソルページネーションは「次へ」と「前へ」のリンクを表示するためにのみ使用でき、ページ番号を持つリンクを生成することはできません。- 順序付けが少なくとも1つの一意の列または一意の列の組み合わせに基づいている必要があります。
null
値を持つ列はサポートされていません。 - 「order by」句のクエリ式は、エイリアス化されて「select」句にも追加されている場合にのみサポートされます。
- パラメータを持つクエリ式はサポートされていません。
ページネータを手動で作成する¶
メモリ内に既にあるアイテムの配列を使用して、ページネーションインスタンスを手動で作成したい場合があります。これを行うには、Illuminate\Pagination\Paginator
、Illuminate\Pagination\LengthAwarePaginator
、またはIlluminate\Pagination\CursorPaginator
インスタンスを作成します。これは、ニーズに応じて異なります。
Paginator
とCursorPaginator
クラスは、結果セット内のアイテムの総数を知る必要がありません。ただし、このため、これらのクラスには最後のページのインデックスを取得するメソッドがありません。LengthAwarePaginator
は、Paginator
とほぼ同じ引数を受け入れますが、結果セット内のアイテムの総数のカウントが必要です。
言い換えれば、Paginator
はクエリビルダのsimplePaginate
メソッドに対応し、CursorPaginator
はcursorPaginate
メソッドに対応し、LengthAwarePaginator
はpaginate
メソッドに対応します。
Warning
ページネーターインスタンスを手動で作成する場合、ページネーターに渡す結果の配列を手動で「スライス」する必要があります。これを行う方法がわからない場合は、array_slice PHP関数を確認してください。
ページネーションURLのカスタマイズ¶
デフォルトでは、ページネーターによって生成されるリンクは現在のリクエストのURIに一致します。しかし、ページネーターのwithPath
メソッドを使用すると、ページネーターがリンクを生成する際に使用するURIをカスタマイズできます。例えば、ページネーターがhttp://example.com/admin/users?page=N
のようなリンクを生成するようにしたい場合、/admin/users
をwithPath
メソッドに渡す必要があります:
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->withPath('/admin/users');
// ...
});
クエリ文字列値の追加¶
appends
メソッドを使用して、ページネーションリンクのクエリ文字列に追加することができます。例えば、各ページネーションリンクにsort=votes
を追加するには、次のようにappends
を呼び出す必要があります:
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->appends(['sort' => 'votes']);
// ...
});
現在のリクエストのすべてのクエリ文字列値をページネーションリンクに追加したい場合は、withQueryString
メソッドを使用できます:
$users = User::paginate(15)->withQueryString();
ハッシュフラグメントの追加¶
ページネーターによって生成されるURLに「ハッシュフラグメント」を追加する必要がある場合は、fragment
メソッドを使用できます。例えば、各ページネーションリンクの末尾に#users
を追加するには、次のようにfragment
メソッドを呼び出す必要があります:
$users = User::paginate(15)->fragment('users');
ページネーション結果の表示¶
paginate
メソッドを呼び出すと、Illuminate\Pagination\LengthAwarePaginator
のインスタンスが返され、simplePaginate
メソッドを呼び出すと、Illuminate\Pagination\Paginator
のインスタンスが返されます。また、cursorPaginate
メソッドを呼び出すと、Illuminate\Pagination\CursorPaginator
のインスタンスが返されます。
これらのオブジェクトは、結果セットを説明するいくつかのメソッドを提供します。これらのヘルパーメソッドに加えて、ページネーターインスタンスはイテレータであり、配列としてループできます。したがって、結果を取得したら、Bladeを使用して結果を表示し、ページリンクをレンダリングできます:
<div class="container">
@foreach ($users as $user)
{{ $user->name }}
@endforeach
</div>
{{ $users->links() }}
links
メソッドは、結果セットの残りのページへのリンクをレンダリングします。これらの各リンクには、適切なpage
クエリ文字列変数が既に含まれています。links
メソッドによって生成されるHTMLは、Tailwind CSSフレームワークと互換性があることを覚えておいてください。
ページネーションリンクウィンドウの調整¶
ページネーターがページネーションリンクを表示するとき、現在のページ番号と、現在のページの前後3ページのリンクが表示されます。onEachSide
メソッドを使用すると、ページネーターが生成する中央のスライディングウィンドウ内のリンクの両側に表示される追加リンクの数を制御できます:
結果をJSONに変換¶
Laravelのページネータークラスは、Illuminate\Contracts\Support\Jsonable
インターフェース契約を実装し、toJson
メソッドを公開しているため、ページネーション結果をJSONに変換するのは非常に簡単です。ルートまたはコントローラーアクションから返すことで、ページネーターインスタンスをJSONに変換することもできます:
use App\Models\User;
Route::get('/users', function () {
return User::paginate();
});
ページネーターからのJSONには、total
、current_page
、last_page
などのメタ情報が含まれます。結果レコードはJSON配列のdata
キーを介して利用できます。ルートからページネーターインスタンスを返すことで作成されるJSONの例を次に示します:
{
"total": 50,
"per_page": 15,
"current_page": 1,
"last_page": 4,
"first_page_url": "http://laravel.app?page=1",
"last_page_url": "http://laravel.app?page=4",
"next_page_url": "http://laravel.app?page=2",
"prev_page_url": null,
"path": "http://laravel.app",
"from": 1,
"to": 15,
"data":[
{
// Record...
},
{
// Record...
}
]
}
ページネーションビューのカスタマイズ¶
デフォルトでは、ページネーションリンクを表示するためにレンダリングされるビューは、Tailwind CSSフレームワークと互換性があります。ただし、Tailwindを使用していない場合は、これらのリンクをレンダリングするために独自のビューを自由に定義できます。ページネーターインスタンスでlinks
メソッドを呼び出すときに、ビュー名をメソッドの最初の引数として渡すことができます:
{{ $paginator->links('view.name') }}
<!-- ビューに追加データを渡す... -->
{{ $paginator->links('view.name', ['foo' => 'bar']) }}
ただし、ページネーションビューをカスタマイズする最も簡単な方法は、vendor:publish
コマンドを使用してresources/views/vendor
ディレクトリにエクスポートすることです:
このコマンドは、ビューをアプリケーションのresources/views/vendor/pagination
ディレクトリに配置します。このディレクトリ内のtailwind.blade.php
ファイルは、デフォルトのページネーションビューに対応します。このファイルを編集して、ページネーションHTMLを変更できます。
別のファイルをデフォルトのページネーションビューとして指定したい場合は、App\Providers\AppServiceProvider
クラスのboot
メソッド内でページネーターのdefaultView
およびdefaultSimpleView
メソッドを呼び出すことができます:
<?php
namespace App\Providers;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::defaultView('view-name');
Paginator::defaultSimpleView('view-name');
}
}
Bootstrapの使用¶
Laravelには、Bootstrap CSSを使用して構築されたページネーションビューも含まれています。これらのビューをデフォルトのTailwindビューの代わりに使用するには、App\Providers\AppServiceProvider
クラスのboot
メソッド内でページネーターのuseBootstrapFour
またはuseBootstrapFive
メソッドを呼び出すことができます:
use Illuminate\Pagination\Paginator;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::useBootstrapFive();
Paginator::useBootstrapFour();
}
ページネーター / LengthAwarePaginatorインスタンスメソッド¶
各ページネーターインスタンスは、以下のメソッドを介して追加のページネーション情報を提供します:
メソッド | 説明 |
---|---|
$paginator->count() |
現在のページのアイテム数を取得します。 |
$paginator->currentPage() |
現在のページ番号を取得します。 |
$paginator->firstItem() |
結果内の最初のアイテムの結果番号を取得します。 |
$paginator->getOptions() |
ページネーターオプションを取得します。 |
$paginator->getUrlRange($start, $end) |
ページネーションURLの範囲を作成します。 |
$paginator->hasPages() |
複数のページに分割するのに十分なアイテムがあるかどうかを判断します。 |
$paginator->hasMorePages() |
データストアにさらにアイテムがあるかどうかを判断します。 |
$paginator->items() |
現在のページのアイテムを取得します。 |
$paginator->lastItem() |
結果内の最後のアイテムの結果番号を取得します。 |
$paginator->lastPage() |
最後の利用可能なページのページ番号を取得します。(simplePaginate を使用する場合は利用できません) |
$paginator->nextPageUrl() |
次のページのURLを取得します。 |
$paginator->onFirstPage() |
ページネーターが最初のページにあるかどうかを判断します。 |
$paginator->perPage() |
ページごとに表示されるアイテムの数。 |
$paginator->previousPageUrl() |
前のページのURLを取得します。 |
$paginator->total() |
データストア内の一致するアイテムの総数を判断します。(simplePaginate を使用する場合は利用できません) |
$paginator->url($page) |
指定されたページ番号のURLを取得します。 |
$paginator->getPageName() |
ページを格納するために使用されるクエリ文字列変数を取得します。 |
$paginator->setPageName($name) |
ページを格納するために使用されるクエリ文字列変数を設定します。 |
$paginator->through($callback) |
コールバックを使用して各アイテムを変換します。 |
カーソルページネーターインスタンスメソッド¶
各カーソルページネーターインスタンスは、以下のメソッドを介して追加のページネーション情報を提供します:
メソッド | 説明 |
---|---|
$paginator->count() |
現在のページのアイテム数を取得します。 |
$paginator->currentPage() |
現在のページ番号を取得します。 |
$paginator->firstItem() |
結果内の最初のアイテムの結果番号を取得します。 |
$paginator->getOptions() |
ページネーターオプションを取得します。 |
$paginator->getUrlRange($start, $end) |
ページネーションURLの範囲を作成します。 |
$paginator->hasPages() |
複数のページに分割するのに十分なアイテムがあるかどうかを判断します。 |
$paginator->hasMorePages() |
データストアにさらにアイテムがあるかどうかを判断します。 |
$paginator->items() |
現在のページのアイテムを取得します。 |
$paginator->lastItem() |
結果内の最後のアイテムの結果番号を取得します。 |
$paginator->lastPage() |
最後の利用可能なページのページ番号を取得します。(simplePaginate を使用する場合は利用できません) |
$paginator->nextPageUrl() |
次のページのURLを取得します。 |
$paginator->onFirstPage() |
ページネーターが最初のページにあるかどうかを判断します。 |
$paginator->perPage() |
ページごとに表示されるアイテムの数。 |
$paginator->previousPageUrl() |
前のページのURLを取得します。 |
$paginator->total() |
データストア内の一致するアイテムの総数を判断します。(simplePaginate を使用する場合は利用できません) |
$paginator->url($page) |
指定されたページ番号のURLを取得します。 |
$paginator->getPageName() |
ページを格納するために使用されるクエリ文字列変数を取得します。 |
$paginator->setPageName($name) |
ページを格納するために使用されるクエリ文字列変数を設定します。 |
$paginator->through($callback) |
コールバックを使用して各アイテムを変換します。 |
メソッド | 説明 |
---|---|
$paginator->count() |
現在のページのアイテム数を取得します。 |
$paginator->cursor() |
現在のカーソルインスタンスを取得します。 |
$paginator->getOptions() |
ページネータのオプションを取得します。 |
$paginator->hasPages() |
複数のページに分割するのに十分なアイテムがあるかどうかを判定します。 |
$paginator->hasMorePages() |
データストアにさらにアイテムがあるかどうかを判定します。 |
$paginator->getCursorName() |
カーソルを保存するために使用されるクエリ文字列変数を取得します。 |
$paginator->items() |
現在のページのアイテムを取得します。 |
$paginator->nextCursor() |
次のアイテムセットのカーソルインスタンスを取得します。 |
$paginator->nextPageUrl() |
次のページのURLを取得します。 |
$paginator->onFirstPage() |
ページネータが最初のページにあるかどうかを判定します。 |
$paginator->onLastPage() |
ページネータが最後のページにあるかどうかを判定します。 |
$paginator->perPage() |
1ページあたりに表示されるアイテム数を取得します。 |
$paginator->previousCursor() |
前のアイテムセットのカーソルインスタンスを取得します。 |
$paginator->previousPageUrl() |
前のページのURLを取得します。 |
$paginator->setCursorName() |
カーソルを保存するために使用されるクエリ文字列変数を設定します。 |
$paginator->url($cursor) |
指定されたカーソルインスタンスのURLを取得します。 |