ページ送り(ページネーション)
データを1ページ分ずつに分けて表示するページ送り(ページネーション)の、使い方・リンクの表示・カーソル方式・見た目の変え方を説明します。
ページ送り(ページネーション)は、たくさんのデータを、1ページに少しずつ表示して、「次へ」「前へ」やページ番号のリンクで移れるようにするしくみです。本の目次を、何ページかに分けて見せるようなものです。
Laravel のページ送りは、クエリビルダ(SQL を書かずに、メソッドをつないでデータベースに問い合わせるしくみ)と、Eloquent(データベースの表を、PHP から扱いやすくしたモデルのしくみ)の両方に組みこまれています。設定をしなくても、すぐ使えます。
ページ送りが作る HTML は、はじめは Tailwind CSS(CSS の道具)に合わせてあります。Bootstrap(別の CSS の道具)に合わせることもできます。
Tailwind と使うとき#
Laravel が用意したページ送りの画面を Tailwind 4.x で使うなら、準備は要りません。アプリの resources/css/app.css に、ページ送りの画面を Tailwind に読ませる @source の行が、はじめから書いてあります。
@import 'tailwindcss';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
基本の使い方#
クエリビルダの結果をページに分ける#
ページに分ける方法はいくつかあります。いちばんかんたんなのは、クエリビルダか Eloquent の問い合わせで、paginate メソッドを使うことです。paginate は、いま見ているページに合わせて、問い合わせの「limit」(取り出す数)と「offset」(読み飛ばす数)を、自動で決めます。
いま何ページ目かは、リクエストのクエリ文字列(URL の ? のあとの部分)の page から分かります。この値は Laravel が自動で見つけ、ページ送りが作るリンクにも自動で入ります。
次の例では、paginate に、1ページに出す数を渡しています。ここでは 15 件です。
<?php
namespace App\Http\Controllers;
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 は、データを取る前に、条件に合う全部の件数を数えます。ページが全部で何ページあるかを、知るためです。でも、全部のページ数を画面に出さないなら、この件数を数える問い合わせは、無駄になります。
「次へ」と「前へ」のリンクだけでよいときは、simplePaginate を使うと、1回の問い合わせで済み、効率がよくなります。
$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 で、カーソル方式(次の節)のページ送りもできます。
$users = User::where('votes', '>', 100)->cursorPaginate(15);
1つの画面に、ページ送りが複数あるとき#
1つの画面に、ページ送りを2つ出したいことがあります。でも、2つとも、クエリ文字列の page にいまのページを入れると、ぶつかります。ぶつからないように、paginate・simplePaginate・cursorPaginate の3つ目の引数に、使うクエリ文字列の名前を渡せます。
use App\Models\User;
$users = User::where('votes', '>', 100)->paginate(
$perPage = 15, $columns = ['*'], $pageName = 'users'
);
| メソッド | 説明 |
|---|---|
paginate |
ページ番号のリンクが出せる。全部の件数を数える |
simplePaginate |
「次へ」と「前へ」だけ。件数を数えないので効率がよい |
cursorPaginate |
カーソル方式。「次へ」と「前へ」だけ。大量のデータ向き |
カーソル方式のページ送り#
paginate と simplePaginate は、SQL の「offset」(読み飛ばし)を使って問い合わせます。カーソル方式は、ちがいます。並べ替えた列の値を比べる「where」(条件)を作って問い合わせます。Laravel のページ送りの中で、いちばんデータベースの負担が少ない方法です。データが多いときや、「無限スクロール」(下へ動かすと続きが出る画面)に、向いています。
offset の方式では、ページ番号が、リンクのクエリ文字列に入ります。カーソル方式では、「カーソル」という文字が入ります。カーソルは、次の問い合わせをどこから始めるか、どちらの向きに進むかを、短い文字に変えて(エンコードして)まとめたものです。
http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0
クエリビルダの cursorPaginate メソッドで、カーソル方式のページ送りを作れます。Illuminate\Pagination\CursorPaginator が返ります。
$users = DB::table('users')->orderBy('id')->cursorPaginate(15);
作ったあとは、paginate と simplePaginate のときと同じに、結果を表示(下の「ページ送りの結果を表示する」の節)できます。カーソル方式のメソッドは、このページの最後の表にあります。
注意
カーソル方式を使うには、問い合わせに「order by」(並べ替え)が必要です。また、並べ替えに使う列は、ページに分ける表の列でなければなりません。
カーソルと offset のちがい#
2つの方式のちがいを、SQL で見てみます。次の2つは、どちらも、users の表を id 順に並べたときの「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;
カーソル方式には、offset の方式にない、よい点があります。
- データが多いとき、並べ替えの列に索引(検索を速くするしくみ)があれば、速くなる。offset は、それまでの分を、全部読み飛ばしながら探すため
- 書きこみが多いデータでは、offset の方式だと、ユーザーが見ているページの内容が、追加や削除で変わったときに、行が抜けたり、重なって出たりすることがある
一方、カーソル方式には、次の制限があります。
simplePaginateと同じく、「次へ」と「前へ」のリンクだけで、ページ番号のリンクは作れない- 重ならない列を、少なくとも1つ使った並べ替えが必要(重ならない列の組み合わせでもよい)。
nullを含む列は使えない - 「order by」の式は、別名を付けて「select」にも加えたときだけ使える
- 引数を持つ式は使えない
ページ送りを手で作る#
データベースから取ったものではなく、プログラムの中にすでにある配列で、ページ送りを手で作りたいことがあります。必要に応じて、Illuminate\Pagination\Paginator・Illuminate\Pagination\LengthAwarePaginator・Illuminate\Pagination\CursorPaginator のどれかを作ります。
Paginator と CursorPaginator は、全部の件数を知る必要がありません。その代わり、最後のページの番号を取り出すメソッドがありません。LengthAwarePaginator は、ほとんど同じ引数ですが、全部の件数が要ります。
つまり、クエリビルダの simplePaginate が Paginator、cursorPaginate が CursorPaginator、paginate が LengthAwarePaginator に対応します。
注意
手でページ送りを作るときは、渡す配列を、自分で「切り分け」る必要があります。やり方が分からなければ、PHP の array_slice 関数を調べてください。
リンクの URL を変える#
はじめは、ページ送りが作るリンクは、いまのリクエストの URI(/users のような、URL の中で場所を表す部分)と同じになります。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 を足すには、次のように書きます。
use App\Models\User;
Route::get('/users', function () {
$users = User::paginate(15);
$users->appends(['sort' => 'votes']);
// ...
});
いまのリクエストのクエリ文字列を、全部足したいときは、withQueryString を使います。
$users = User::paginate(15)->withQueryString();
ハッシュ(#)を足す#
リンクの最後に、ハッシュ(# のあとの、ページの中の場所を示す部分)を足したいときは、fragment メソッドを使います。たとえば、全部のリンクの最後に #users を足すには、次のように書きます。
$users = User::paginate(15)->fragment('users');
| メソッド | 説明 |
|---|---|
withPath |
リンクに使う URI を変える |
appends |
リンクのクエリ文字列に、決めた値を足す |
withQueryString |
いまのリクエストのクエリ文字列を、全部リンクに足す |
fragment |
リンクの最後に、ハッシュを足す |
ページ送りの結果を表示する#
paginate を呼ぶと Illuminate\Pagination\LengthAwarePaginator、simplePaginate を呼ぶと Illuminate\Pagination\Paginator、cursorPaginate を呼ぶと Illuminate\Pagination\CursorPaginator が返ります。
これらには、結果のようす(全部で何件か、いま何ページ目か、など)を知るメソッドがあります。また、配列と同じように @foreach でくり返せます。取り出した結果の表示と、ページのリンクの表示は、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 にする#
ページ送りのクラスは、Illuminate\Contracts\Support\Jsonable というインターフェース(クラスが持つべきメソッドの決まり)に従っていて、toJson メソッドを持っています。そのため、結果をかんたんに JSON(データを文字で表す形式)にできます。ルートやコントローラーから、ページ送りをそのまま返しても、JSON になります。
use App\Models\User;
Route::get('/users', function () {
return User::paginate();
});
JSON には、total・current_page・last_page など、ページ送りの情報(メタ情報)が入ります。結果の行は、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 メソッドの1つ目の引数に、ビューの名前を渡します。
{{ $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();
}
| メソッド | 説明 |
|---|---|
defaultView |
はじめのページ送りの画面を、別のビューにする |
defaultSimpleView |
かんたんなページ送り用の、はじめの画面を、別のビューにする |
useBootstrapFive |
Bootstrap 5 用の画面を使う |
useBootstrapFour |
Bootstrap 4 用の画面を使う |
Paginator と LengthAwarePaginator のメソッド#
ページ送りのオブジェクト($paginator)には、次のメソッドがあります。$paginator->count() のように呼びます。
| メソッド | 説明 |
|---|---|
count() |
いまのページの件数 |
currentPage() |
いまのページの番号 |
firstItem() |
結果の中で、最初の行の番号 |
getOptions() |
ページ送りの設定を取り出す |
getUrlRange($start, $end) |
ページの URL を、範囲でまとめて作る |
hasPages() |
複数のページに分けるほど、行があるか |
hasMorePages() |
データの置き場に、まだ続きがあるか |
items() |
いまのページの行を取り出す |
lastItem() |
結果の中で、最後の行の番号 |
lastPage() |
最後のページの番号(simplePaginate では使えない) |
nextPageUrl() |
次のページの URL |
onFirstPage() |
最初のページにいるか |
onLastPage() |
最後のページにいるか |
perPage() |
1ページに出す件数 |
previousPageUrl() |
前のページの URL |
total() |
条件に合う、全部の件数(simplePaginate では使えない) |
url($page) |
指定したページの URL |
getPageName() |
ページ番号を入れる、クエリ文字列の名前を取り出す |
setPageName($name) |
ページ番号を入れる、クエリ文字列の名前を決める |
through($callback) |
関数で、1つ1つの行を変える |
Cursor Paginator のメソッド#
カーソル方式のページ送りのオブジェクトには、次のメソッドがあります。
| メソッド | 説明 |
|---|---|
count() |
いまのページの件数 |
cursor() |
いまのカーソルを取り出す |
getOptions() |
ページ送りの設定を取り出す |
hasPages() |
複数のページに分けるほど、行があるか |
hasMorePages() |
データの置き場に、まだ続きがあるか |
getCursorName() |
カーソルを入れる、クエリ文字列の名前を取り出す |
items() |
いまのページの行を取り出す |
nextCursor() |
次の行の組のカーソルを取り出す |
nextPageUrl() |
次のページの URL |
onFirstPage() |
最初のページにいるか |
onLastPage() |
最後のページにいるか |
perPage() |
1ページに出す件数 |
previousCursor() |
前の行の組のカーソルを取り出す |
previousPageUrl() |
前のページの URL |
setCursorName() |
カーソルを入れる、クエリ文字列の名前を決める |
url($cursor) |
指定したカーソルの URL |
関連するページ#
公式ドキュメント(英語)
2026年10月5日時点の内容をもとに、日本語でまとめています。