トランソニックソフトウェア

ブログ
BLOG

2023.10.27

【エンジニア沼日記】#01 Laravelページネーション

Laravelのページネーション機能

Laravelではページネーションが簡単にできる!

ページネーションとは、多数のデータや情報を分割して表示する為の技術です。
Laravelでは標準で簡単に実装できる機能が組み込まれています

例えば、ユーザの一覧画面にページネーションを実装する場合、まずコントローラーで以下のようにモデルのpaginateメソッドを使います。

$users = App\Models\User::paginate(15)

そして、bladeでlinksメソッドを呼び出します。

{{ $users->links() }}

こうすることで、ユーザの情報を15件ずつ取りだして、ページを移動するためのボタンまで表示できます。

柔軟にカスタマイズもできる!

linksメソッドの引数にbladeのパスを指定する事で、ページネーションのボタンを自由にカスタマイズできます。

{{ $users->links(‘pagination.customize’) }}

これでボタンの配置や色も、HTML&CSSでデザインできるというわけです。

自由度が高い分、難易度もあがる!?

筆者は、カスタマイズで沼にはまりました。

3ページ分のリンクと、更にページが続く場合はThree Dotsを入れるページネーションを作成しているときのことです。

一見簡単そうに思えますが、

  • 現在のページが中間の10ページだったら・・・
  • 最初のページと最後のページだったら端の色を変えたい・・・
  • Three Dotsは後に続く場合のみ入れたい・・・

などなど、条件はなかなか複雑です。Laravelのドキュメントを読みながら、悪戦苦闘して何とか実装できました。

Laravelのページネーション機能は、Paginatorクラスが担っています。bladeには、「paginator」と次ページの情報やThree Dotsなどが格納されている「elements」配列が渡されます。Paginatorには様々なメソッドが用意されているので、それらを駆使して実装していきます。

最終的には以下のようになりました。


{{-- ページネーションアイテムの判定 --}}
@if ($paginator->hasPages())
    <ul class="pagination">
        {{-- 最初のページを判定 --}}
        @if (!$paginator->onFirstPage())
            <!-- Previous page link -->
            <li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">&lsaquo;</a></li>
        @else
            <li class="disabled"><span>&lsaquo;</span></li>
        @endif
        @php
            $current = $paginator->currentPage();
            $slice = [];
        @endphp
        {{-- pagination Elements --}}
        @foreach ($elements as $key => $element)
            {{-- elementの判定 --}}
            {{-- 配列 --}}
            @if (is_array($element) && array_key_exists($current, $element))
                @php
                    if ($paginator->onFirstPage()) {
                        // 最初のページ処理
                        for ($i = 1; $i <= 3 && $i <= $paginator->lastPage(); $i++) {
                            $slices[$i] = $elements[0][$i] ?? null;
                        }
                    } else {
                        // 中間ページ処理
                        /* @var Array $page_keys   対象の配列のキーを取得 */
                        $page_keys = array_keys($element);
                        /* @var Int $current_page  現在のページキーを取得*/
                        $current_index = array_search($current, $page_keys);
if ($paginator->hasMorePages()) {
                            // 前後1ページずつの取得
                            $start = max($current_index - 1, 0);
                            $end = min($current_index + 1, count($page_keys) - 1);
                        } else {
                            // 最終ページの場合は、前の2ページと現在のページを取得
                            $start = max($current_index - 2, 0);
                            $end = $current_index;
                        }
for ($i = $start; $i <= $end; $i++) {
                            $slices[$page_keys[$i]] = $element[$page_keys[$i]];
                        }
                    }
                @endphp
                @foreach ($slices as $page => $url)
                    @if ($page == $paginator->currentPage())
                        <li class="active">{{ $page }}</li>
                    @else
                        <li><a href="{{ $url }}">{{ $page }}</a></li>
                    @endif
                @endforeach
            @elseif (is_string($element))
                @php
                    // 比較対象のキー
                    $comparison_key = getCurrentToElements($current, $elements);
                @endphp
@if ($key > $comparison_key)
                    {{-- Three Dotsの表示 --}}
                    <li>{{ $element }}</li>
                @endif
            @endif
        @endforeach
        {{-- 次へのリンク --}}
        @if ($paginator->hasMorePages())
            <li><a href="{{ $paginator->nextPageUrl() }}" rel="next">&rsaquo;</a></li>
        @else
            <li class="disabled"><span>&rsaquo;</span></li>
        @endif
@endif
</ul>
{{-- 内部メソッド --}}
@php
    /**
     *現在のURLが格納されている配列のキーを取得
     *
     * @param   Int     $current     現在のページインデント
     * @param   Array   $elements    $elements
     * @return  Int     $key
     */
    function getCurrentToElements($current, $elements)
    {
        foreach ($elements as $key => $element) {
            if (is_array($element)) {
                if (array_key_exists($current, $element)) {
                    return $key;
                }
            }
        }
    }
@endphp

それでは、次の沼へハマりに行きます…