2023.10.27
ページネーションとは、多数のデータや情報を分割して表示する為の技術です。
Laravelでは標準で簡単に実装できる機能が組み込まれています
例えば、ユーザの一覧画面にページネーションを実装する場合、まずコントローラーで以下のようにモデルのpaginateメソッドを使います。
$users = App\Models\User::paginate(15)
そして、bladeでlinksメソッドを呼び出します。
{{ $users->links() }}
こうすることで、ユーザの情報を15件ずつ取りだして、ページを移動するためのボタンまで表示できます。
linksメソッドの引数にbladeのパスを指定する事で、ページネーションのボタンを自由にカスタマイズできます。
{{ $users->links(‘pagination.customize’) }}
これでボタンの配置や色も、HTML&CSSでデザインできるというわけです。
筆者は、カスタマイズで沼にはまりました。
3ページ分のリンクと、更にページが続く場合は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">‹</a></li>
@else
<li class="disabled"><span>‹</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">›</a></li>
@else
<li class="disabled"><span>›</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
それでは、次の沼へハマりに行きます…