データベース: ページネーション (Database: Pagination)
- Introduction
- 基本的な使い方
- ページネーション結果の表示
- ページネーションビューのカスタマイズ
- Paginator および LengthAwarePaginator インスタンス メソッド
- カーソル ページネータ インスタンス メソッド
導入 (Introduction)
他のフレームワークでは、ページネーションは非常に面倒な場合があります。 Laravel のページネーションへのアプローチが新風となることを願っています。 Laravel のページネータは クエリビルダ および Eloquent ORM と統合されており、設定なしでデータベース レコードの便利で使いやすいページネーションを提供します。
デフォルトでは、ページネータによって生成された HTML は Tailwind CSS フレームワーク と互換性があります。ただし、Bootstrap ページネーションのサポートも利用できます。
Tailwind JIT
Laravel のデフォルトの Tailwind ページネーション ビューと Tailwind JIT エンジンを使用している場合は、Tailwind クラスがパージされないように、アプリケーションの tailwind.config.js ファイルの content キーが Laravel のページネーション ビューを参照していることを確認する必要があります。
content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
],
基本的な使い方 (Basic Usage)
クエリビルダ結果のページ分割
アイテムをページ分割するにはいくつかの方法があります。最も簡単な方法は、クエリビルダ または Eloquent クエリ で paginate メソッドを使用することです。 paginate メソッドは、ユーザーが表示している現在のページに基づいてクエリの「制限」と「オフセット」の設定を自動的に処理します。デフォルトでは、現在のページは、HTTP リクエストの page クエリ文字列引数の値によって検出されます。この値は Laravel によって自動的に検出され、ページネータによって生成されたリンクにも自動的に挿入されます。
この例では、paginate メソッドに渡される唯一の引数は、「ページごとに」表示する項目の数です。この場合、ページごとに 15 アイテムを表示することを指定しましょう。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
/**
* Show all application users.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
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 モデルをページ分割し、1 ページあたり 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);
同様に、cursorPaginate メソッドを使用して、Eloquent モデルをカーソルでページネーションすることもできます。
$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 のクエリ文字列にページ番号が含まれるオフセット ベースのページネーションとは異なり、カーソル ベースのページネーションでは、クエリ文字列に「カーソル」文字列が配置されます。カーソルは、次のページ分割されたクエリがページ分割を開始する位置とページ分割の方向を含むエンコードされた文字列です。
http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0
クエリビルダが提供する cursorPaginate メソッドを使用して、カーソル ベースのページネータ インスタンスを作成できます。このメソッドは、Illuminate\Pagination\CursorPaginator のインスタンスを返します。
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
カーソル ページネータ インスタンスを取得したら、paginate および simplePaginate メソッドを使用するときに通常行うのと同じように、ページネーションの結果を表示する を実行できます。カーソル ページネータによって提供されるインスタンス メソッドの詳細については、カーソル ページネータ インスタンス メソッドのドキュメント を参照してください。
警告 カーソルのページネーションを利用するには、クエリに「order by」句が含まれている必要があります。
カーソルとオフセットのページネーション
オフセット ページネーションとカーソル ページネーションの違いを説明するために、いくつかの SQL クエリの例を見てみましょう。次のクエリはどちらも、id で順序付けされた users テーブルの結果の「2 ページ目」を表示します。
# Offset Pagination...
select * from users order by id asc limit 15 offset 15;
# Cursor Pagination...
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 メソッドに対応します。
警告 ページネータ インスタンスを手動で作成する場合は、ページネータに渡す結果の配列を手動で「スライス」する必要があります。これを行う方法がわからない場合は、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');
ページネーション結果の表示 (Displaying Pagination Results)
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 メソッドを使用すると、ページネータによって生成されたリンクの中央のスライディング ウィンドウ内の現在のページの両側に表示される追加リンクの数を制御できます。
{{ $users->onEachSide(5)->links() }}
結果を 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...
}
]
}
ページネーションビューのカスタマイズ (Customizing The Pagination View)
デフォルトでは、ページネーション リンクを表示するためにレンダリングされるビューは、Tailwind CSS フレームワークと互換性があります。ただし、Tailwind を使用していない場合は、これらのリンクをレンダリングする独自のビューを自由に定義できます。ページネータ インスタンスで links メソッドを呼び出す場合、最初の引数としてビュー名をメソッドに渡すことができます。
{{ $paginator->links('view.name') }}
<!-- Passing additional data to the view... -->
{{ $paginator->links('view.name', ['foo' => 'bar']) }}
ただし、ページネーション ビューをカスタマイズする最も簡単な方法は、vendor:publish コマンドを使用してページネーション ビューを resources/views/vendor ディレクトリにエクスポートすることです。
php artisan vendor:publish --tag=laravel-pagination
このコマンドは、アプリケーションの 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.
*
* @return void
*/
public function boot()
{
Paginator::defaultView('view-name');
Paginator::defaultSimpleView('view-name');
}
}
ブートストラップの使用
Laravel には、ブートストラップCSS を使用して構築されたページ分割ビューが含まれています。デフォルトの Tailwind ビューの代わりにこれらのビューを使用するには、App\Providers\AppServiceProvider クラスの boot メソッド内で、ページネータの useBootstrapFour メソッドまたは useBootstrapFive メソッドを呼び出すことができます。
use Illuminate\Pagination\Paginator;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Paginator::useBootstrapFive();
Paginator::useBootstrapFour();
}
Paginator / LengthAwarePaginator インスタンス メソッド (Paginator / LengthAwarePaginator Instance Methods)
各ページネータ インスタンスは、次のメソッドを介して追加のページネーション情報を提供します。
| 方法 | 説明 |
|---|---|
$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) | ページの保存に使用されるクエリ文字列変数を設定します。 |
カーソル ページネータ インスタンス メソッド (Cursor Paginator Instance Methods)
各カーソル ページネーション インスタンスは、次のメソッドを介して追加のページネーション情報を提供します。
| 方法 | 説明 |
|---|---|
$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() | ページごとに表示される項目の数。 |
$paginator->previousCursor() | 前の項目セットのカーソル インスタンスを取得します。 |
$paginator->previousPageUrl() | 前のページの URL を取得します。 |
$paginator->setCursorName() | カーソルを格納するために使用されるクエリ文字列変数を設定します。 |
$paginator->url($cursor) | 指定されたカーソル インスタンスの URL を取得します。 |