Skip to content

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

イントロダクション

他のフレームワークでは、ページネーションは非常に苦痛なものになることがあります。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つのページネータは競合します。この競合を解決するには、paginatesimplePaginate、およびcursorPaginateメソッドに提供される3番目の引数に、ページネータの現在のページを格納するために使用するクエリ文字列パラメータの名前を渡します。

use App\Models\User;

$users = User::where('votes', '>', 100)->paginate(
    $perPage = 15, $columns = ['*'], $pageName = 'users'
);

カーソルページネーション

paginatesimplePaginateはSQLの「offset」句を使用してクエリを作成しますが、カーソルページネーションはクエリに含まれる順序付けられた列の値を比較する「where」句を構築することで機能し、Laravelのすべてのページネーション方法の中で最も優れたデータベースパフォーマンスを提供します。このページネーション方法は、特に大規模なデータセットと「無限」スクロールユーザーインターフェースに適しています。

オフセットベースのページネーションとは異なり、カーソルベースのページネーションはページネータによって生成されるURLのクエリ文字列にページ番号を含めません。代わりに、クエリ文字列に「カーソル」文字列を配置します。カーソルは、次のページネーションクエリがページネーションを開始する場所と方向を含むエンコードされた文字列です。

http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

クエリビルダによって提供される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\PaginatorIlluminate\Pagination\LengthAwarePaginator、またはIlluminate\Pagination\CursorPaginatorインスタンスを作成します。これは、ニーズに応じて異なります。

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

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

Warning

ページネーターインスタンスを手動で作成する場合、ページネーターに渡す結果の配列を手動で「スライス」する必要があります。これを行う方法がわからない場合は、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') }}

<!-- ビューに追加データを渡す... -->
{{ $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の使用

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を取得します。

ユーザーノート