メインコンテンツまでスキップ

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

はじめに

他のフレームワークでは、ページネーションは非常に面倒です。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 メソッドに渡される唯一の引数は、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);

同様に、Eloquent モデルをカーソルページネーションする際には cursorPaginate メソッドを使用できます:

    $users = User::where('votes', '>', 100)->cursorPaginate(15);

ページごとの複数のページネータインスタンス

時には、アプリケーションによってレンダリングされる単一の画面に2つの別々のページネータを表示する必要がある場合があります。ただし、両方のページネータインスタンスが現在のページを保存するために page クエリ文字列パラメータを使用する場合、2つのページネータが競合します。この競合を解決するために、paginatesimplePaginate、および cursorPaginate メソッドに提供される3番目の引数を使用して、ページネータの現在のページを保存するために使用するクエリ文字列パラメータの名前を渡すことができます。

    use App\Models\User;

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

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

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

オフセットベースのページネーションとは異なり、ページネーションに基づくカーソルは、ページネーションに使用される次のクエリの開始位置とページネーションの方向を含むエンコードされた文字列をクエリ文字列に配置します。

http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

クエリビルダが提供する cursorPaginate メソッドを使用して、カーソルベースのページネータインスタンスを作成できます。このメソッドは Illuminate\Pagination\CursorPaginator のインスタンスを返します。

    $users = DB::table('users')->orderBy('id')->cursorPaginate(15);

カーソルページネータインスタンスを取得したら、paginate および simplePaginate メソッドを使用する場合と同様に、ページネーション結果を表示 することができます。カーソルページネータが提供するインスタンスメソッドに関する詳細情報については、カーソルページネータインスタンスメソッドのドキュメント を参照してください。

警告

カーソルページネーションを利用するためには、クエリに "order by" 句を含める必要があります。さらに、クエリで並べ替えられる列は、ページネーションされているテーブルに属している必要があります。

カーソル vs. オフセットページネーション

オフセットページネーションとカーソルページネーションの違いを示すために、いくつかの例の SQL クエリを調べてみましょう。次のクエリのいずれも、id で並べ替えられた users テーブルの "2 ページ目" の結果を表示します。


```sql
# 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 と同様に、カーソルベースのページネーションは "Next" と "Previous" リンクの表示にのみ使用でき、ページ番号を持つリンクの生成をサポートしません。
  • オーダリングが少なくとも1つのユニークなカラムまたはユニークな組み合わせに基づいている必要があります。null 値を持つカラムはサポートされていません。
  • "order by" 句内のクエリ式は、エイリアスを付けて "select" 句に追加されている場合のみサポートされます。
  • パラメータを持つクエリ式はサポートされていません。

ページネータを手動で作成する

時には、すでにメモリ内にあるアイテムの配列を渡して、ページネーションインスタンスを手動で作成したい場合があります。必要に応じて、Illuminate\Pagination\PaginatorIlluminate\Pagination\LengthAwarePaginator、または Illuminate\Pagination\CursorPaginator インスタンスを作成することができます。

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

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

警告

手動でページネータのインスタンスを作成する場合は、ページネータに渡す結果の配列を手動で「スライス」する必要があります。これを行う方法がわからない場合は、array_slice PHP 関数を確認してください。

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

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

    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 を使用する

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();
}

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

カーソルページネータのインスタンスメソッド

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

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