第17回 Laravel10 環境構築メモ(LaravelのPagenationの戻り値を強烈にカスタマイズしてみる)

はじめに

現在、しょぼい一覧画面を鋭意、アップグレード中なのですが、とにもかくにも、LaravelのPagenationの戻り値が使いにくいという事でカスタマイズしてみようというのが今日のやりたいことです。

  • スネークをキャメルにしたい

  • 不要なデータは削除&受け取りやすく加工してから画面に戻したい

というのが今日のGoalです。ちなみに現在、第11回のこの一覧画面を

第11回

こういう感じにアップグレード中です。

アップグレード中

今回は、Pagenationのカスタマイズがメインなので、フロントエンドの話は次回くらいにメモする予定です。

カスタマイズ用のクラスを作成

ちなみにこの方法がBestかは分かってないです。が、さらっと調べてでてきた、この本家のページの

ペジネーション情報のカスタマイズ
レスポンスのlinksやmetaキーが持つ情報をカスタマイズしたい場合は、リソースにpaginationInformationメソッドを定義してください。このメソッドは$paginatedデータと、$default情報の配列(links キーと meta キーを含む配列)を引数に受けます。

Laravel 10.x Eloquent:APIリソース

こいつを実現しようと思ったのが始まりです。が、ペロッとResourcesにpaginationInformationを定義してもピクリとも動きませんでした。そして、Googleで検索しても、情報が殆どなく、苦労の割にあんまり、メジャーなカスタマイズじゃないのかもという不安はありますね。。。

それはさておき、で?どうするかというと、最初にAnonymousResourceCollectionを継承したクラスを作ります。この中にpaginationInformationを定義します。詳しくは、copilot作のコメント読んでもらえればと思いますが、中身はどうでもよくて、要はこうすればカスタマイズできるという事だけ理解できたのは収穫かなと。

  • app/Http/Resources/CustomResourceCollection.php

<?php
namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Str;

/**
 * Class CustomResourceCollection
 *
 * This class extends the AnonymousResourceCollection class and provides a custom implementation
 * for handling pagination information in the API responses.
 */
class CustomResourceCollection extends AnonymousResourceCollection
{
    /**
     * Method to customize the pagination information in the API response.
     *
     * @param Request $request The incoming request instance.
     * @param array $paginated The paginated data.
     * @param array $default The default pagination information.
     *
     * @return array The modified pagination information.
     */
    public function paginationInformation(Request $request, array $paginated, array $default): array
    {
        // Number of links to be shown on each side of the current page link.
        $eachSide = 3;

        // Array to hold the filtered links.
        $filteredLinks = [];

        // The original set of links.
        $links = $default['meta']['links'];

        // The current page number.
        $currentPage = $default['meta']['current_page'];

        // If the total number of links is greater than twice the number of links on each side plus one...
        if (count($links) - 2 > ($eachSide * 2) + 1) {
            // If the current page minus the number of links on each side is less than or equal to zero...
            if ($currentPage - $eachSide <= 0) {
                // The start page is the current page.
                $startPage = $currentPage;

                // The end page is the current page plus twice the number of links on each side.
                $endPage = $currentPage + ($eachSide * 2);
            } else {
                // If the current page plus the number of links on each side is greater than the total number of links minus two...
                if ($currentPage + $eachSide > count($links) - 2) {
                    // The start page is the total number of links minus two minus twice the number of links on each side.
                    $startPage = count($links) - 2 - ($eachSide * 2);

                    // The end page is the total number of links minus two.
                    $endPage = count($links) - 2;
                } else {
                    // The start page is the current page minus the number of links on each side.
                    $startPage = $currentPage - $eachSide;

                    // The end page is the current page plus the number of links on each side.
                    $endPage = $currentPage + $eachSide;
                }
            }
        } else {
            // The start page is 1.
            $startPage = 1;

            // The end page is the total number of links minus two.
            $endPage = count($links) - 2;
        }

        // Loop through the links from the start page to the end page and add them to the filtered links array.
        for ($i = $startPage; $i <= $endPage; $i++) {
            $filteredLinks[] = $links[$i];
        }

        // Replace the original links with the filtered links.
        $default['meta']['links'] = $filteredLinks;

        // Convert the keys in the meta array to camel case.
        $default['meta'] = array_combine(
            array_map(static fn(string $k) => Str::camel($k), array_keys($default['meta'])),
            array_values($default['meta'])
        );

        // Set the first, last, next, and previous page links.
        $default['pagination']['first'] = $default['links']['first'];
        $default['pagination']['last'] = $default['links']['last'];
        $default['pagination']['next'] = $default['links']['next'];
        $default['pagination']['prev'] = $default['links']['prev'];

        // Remove the original links array.
        unset($default['links']);

        // Set the filtered links.
        $default['pagination']['links'] = $default['meta']['links'];

        // Remove the meta array.
        unset($default['meta']);

        // Return the modified pagination information.
        return $default;
    }
}

はい、これだけだとまだ動きません。加えて、こんなの作ります。これ何やってるかというとJsonResourceの中でAnonymousResourceCollectionを使ってる所を今回作成したCustomResourceCollectionに変更する為にJsonResourceのnewCollectionをオーバーライドしてます。

  • app/Http/Resources/BaseJsonResource.php

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;

/**
 * Class BaseJsonResource
 *
 * This class extends the JsonResource class and provides a base implementation
 * for JSON resources in the application.
 */
class BaseJsonResource extends JsonResource
{
    /**
     * Creates a new collection of resources.
     *
     * This method is used to create a new collection of resources. It takes a resource as an argument and returns a new instance of the CustomResourceCollection class.
     * The CustomResourceCollection class is used to handle the collection of resources in a custom way.
     *
     * @param mixed $resource The resource to be converted into a collection.
     *
     * @return CustomResourceCollection The created resource collection.
     */
    protected static function newCollection($resource): CustomResourceCollection
    {
        return new CustomResourceCollection($resource, static::class);
    }
}

JsonResourceのcollectionを弄ってる人がいたんですが、たぶん、Laravelのバージョン違いとかで、微妙に作りが変わってるのかもしれません。

はい、最後に第11回の時に作成したGreetingResourceを少し修正します。さっき作ったBaseJsonResourceを継承します。

  • Http/Resources/GreetingResource.php

<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class GreetingResource extends BaseJsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }
}

以上です!!

これ最終的に、CustomResourceCollectionで定義したpaginationInformationがここでお呼ばれしてるみたいですが・・・難しそうなので、深追いしないことに決めました。

  • Illuminate/Http/Resources/Json/PaginatedResourceResponse.php

    protected function paginationInformation($request)
    {
        $paginated = $this->resource->resource->toArray();

        $default = [
            'links' => $this->paginationLinks($paginated),
            'meta' => $this->meta($paginated),
        ];

        if (method_exists($this->resource, 'paginationInformation') ||
            $this->resource->hasMacro('paginationInformation')) {
            return $this->resource->paginationInformation($request, $paginated, $default);
        }

        return $default;
    }

さいごに

結果ですが、もともと↓だったのが、

before

こんな感じになりました。

after

そして、キャメルケースにした変数を結局、一つも使ってないことに気が付きました・・・。


いいなと思ったら応援しよう!