コンテンツへスキップ

データベース:ページネーション

はじめに

他のフレームワークでは、ページネーションは非常に困難な場合があります。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`メソッドは、ユーザーによって表示されている現在のページに基づいて、クエリのリミットとオフセットを自動的に設定します。デフォルトでは、現在のページはHTTPリクエストの`page`クエリ文字列引数の値によって検出されます。この値はLaravelによって自動的に検出され、ページネーターによって生成されたリンクにも自動的に挿入されます。

この例では、`paginate`メソッドに渡される引数は、「1ページあたりのアイテム数」だけです。ここでは、1ページあたり15個のアイテムを表示するように指定します。

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Show all application users.
*/
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`モデルをページネーションし、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`メソッドを使用する場合と同様にページネーション結果を表示できます。カーソルページネーターによって提供されるインスタンスメソッドの詳細については、カーソルページネーターインスタンスメソッドのドキュメントを参照してください。

exclamation

カーソルページネーションを利用するには、クエリに「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\PaginatorIlluminate\Pagination\LengthAwarePaginator、またはIlluminate\Pagination\CursorPaginatorインスタンスを作成することで、これを行うことができます。

PaginatorクラスとCursorPaginatorクラスは、結果セット内のアイテムの総数を認識する必要はありません。ただし、このため、これらのクラスには最終ページのインデックスを取得するためのメソッドがありません。LengthAwarePaginatorPaginatorとほぼ同じ引数を受け付けますが、結果セット内のアイテムの総数のカウントが必要です。

言い換えると、PaginatorはクエリビルダーのsimplePaginateメソッドに対応し、CursorPaginatorcursorPaginateメソッドに対応し、LengthAwarePaginatorpaginateメソッドに対応します。

exclamation

ペジネーターインスタンスを手動で作成する場合は、ペジネーターに渡す結果の配列を手動で「スライス」する必要があります。方法が不明な場合は、array_slice PHP関数を参照してください。

ページネーションURLのカスタマイズ

デフォルトでは、ペジネーターによって生成されるリンクは現在のリクエストのURIと一致します。ただし、ペジネーターのwithPathメソッドを使用すると、リンクを生成するときにペジネーターによって使用されるURIをカスタマイズできます。たとえば、ペジネーターがhttp://example.com/admin/users?page=Nのようなリンクを生成させたい場合は、/admin/userswithPathメソッドに渡します。

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メソッドを使用すると、ペジネーターによって生成された中央のスライディングウィンドウ内の現在のページの各側に表示される追加リンクの数を制御できます。

{{ $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には、totalcurrent_pagelast_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') }}
 
<!-- 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.
*/
public function boot(): void
{
Paginator::defaultView('view-name');
 
Paginator::defaultSimpleView('view-name');
}
}

Bootstrapの使用

Bootstrap CSSを使用して構築されたペジネーションビューがLaravelに含まれています。デフォルトのTailwindビューの代わりにこれらのビューを使用するには、App\Providers\AppServiceProviderクラスのbootメソッド内で、ペジネーターのuseBootstrapFourメソッドまたはuseBootstrapFiveメソッドを呼び出すことができます。

use Illuminate\Pagination\Paginator;
 
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Paginator::useBootstrapFive();
Paginator::useBootstrapFour();
}

Paginator / 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) コールバックを使用して各アイテムを変換します。

Cursor Paginatorのインスタンスメソッド

各カーソルペジネーターインスタンスは、次のメソッドを介して追加のペジネーション情報を提供します。

メソッド 説明
$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を取得します。