【Laravel】API開発でページネーションを実装する

## はじめに

 `フロント側: Next.js` × `API側: Laravel` という構成で開発をしており、フロント側からAPIの `/blogs?page=2` へリクエストすると2ページ目を取得、 `/blogs?page=3` へリクエストすると3ページ目を取得というように、ページネーション に対応したAPIを実装したい場面があった。

 `Laravel` にはページネーション機能があるが、API開発をしているときにページネーションに対応すれば良いのだろう？と悩んで調べたところ、すごく簡単でフレームワークってすごい！と驚いた。

この記事では `Laravel` のページネーション機能の基本的な使い方と、API開発での実装方法をまとめておく。

## コントローラーの記述

最新のブログ記事から順に10件ずつ取得する場合を例に挙げる。

```php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Blog;
use Illuminate\Http\Request;

class BlogController extends Controller
{
    public function index()
    {
        return Blog::latest()->paginate(10);
    }
}
```

Laravelの `paginate` メソッドの基本的な使用方法は、 `paginate` メソッドの引数にページ毎に表示したいアイテム数を渡すだけ。

これだけで、HTTPリクエストのURLのクエリパラメータから現在のページを検出し、 `limit` （1ページで返すデータ数）や `offset` （開始位置）を自動的に処理してくれる。

 `Request`オブジェクトを受け取るとか、クエリパラメータを受け取るような記述は一切必要ない。

もちろん `where` 句などと組み合わせて使用することもできる。

```php
Blog::where('user_id', '1')->paginate(10);
```

## ルーティングの定義

 `api.php` にルーティングを定義する。

```php
<?php

use App\Http\Controllers\Api\BlogController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('blogs', [BlogController::class, 'index']);
```

ここでもクエリパラメータを受け取る記述は必要なく、先述の通り `paginate` メソッドを使ってさえいれば、あとはLaravelが自動的にクエリパラメータを検出して処理してくれる。

## APIリクエストを送ってみる

定義したURL宛にAPIリクエストを送ってみると、以下のような形式のレスポンスが返ってくる。

 `http://localhost/api/blogs` （1ページ目）にアクセスした場合

```json
{
    "items": {
        "current_page": 1,
        "data": [
            {
                "id": 11,
                "title": "タイトル11",
            },
            {
                "id": 10,
                "title": "タイトル10",
            },

            //****** 中略 ******// 

            {
                "id": 2,
                "title": "タイトル2",
            }
        ],
        "first_page_url": "http://localhost/api/blogs?page=1",
        "from": 1,
        "last_page": 2,
        "last_page_url": "http://localhost/api/blogs?page=2",
        "links": [
            {
                "url": null,
                "label": "&laquo; Previous",
                "active": false
            },
            {
                "url": "http://localhost/api/blogs?page=1",
                "label": "1",
                "active": true
            },
            {
                "url": "http://localhost/api/blogs?page=2",
                "label": "2",
                "active": false
            },
            {
                "url": "http://localhost/api/blogs?page=2",
                "label": "Next &raquo;",
                "active": false
            }
        ],
        "next_page_url": "http://localhost/api/blogs?page=2",
        "path": "http://localhost/api/blogs",
        "per_page": "10",
        "prev_page_url": null,
        "to": 10,
        "total": 11
    }
}
```

レスポンスの中の `data` に、ブログ記事データが最新から順に格納されている。

また `"current_page": 1,` の現在ページや、 `"last_page": 2,` の何ページ目まで存在するかだったり、 `"next_page_url": "http://localhost/api/blogs?page=2"` の次ページを取得するためのURLなど、色々な情報を自動的にレスポンスに組み込んでくれている。

　
このように、API側でやることは `paginate` メソッドを使用してDBからデータを取得することのみで、あとはフロント側から1ページ目であれば `/blogs` 、2ページ目であれば `/blogs?page=2` というようにページ数をクエリパラメータで指定してリクエストを送るだけで、ページネーション が実装できる。

## さいごに

 `paginate` メソッドを使うだけであとのことはLaravelが全て自動的に行ってくれて、返されるレスポンスも非常に丁寧で充実していて、これぞフレームワークの便利さ！と感動した。

ただ逆に言えば、 `paginate` メソッドが実装されている箇所を見るまでは、ページネーションに対応したAPIだと一切わからないので、他の開発メンバーがルーティング定義ファイルを見ただけではただの全件取得APIに見えるだろうな…と思ったり。

情報共有やメソッド名の工夫は必要かもしれない。

## 参考記事

https://readouble.com/laravel/8.x/ja/pagination.html

https://zenn.dev/naoki_oshiumi/articles/b894673b759856