Laravelでリンク数を可変で決められるペジネーションの自作方法

Laravelのデフォルトのペジネーションが長さの調整はしにくいわ、treee dotsは邪魔だわ、デザインの変更はしづらいわで絶妙に使いづらかったので自作しました。

# 完成系
こんな感じ
<img width="331" alt="スクリーンショット 2019-05-11 18.40.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391127/0d6d9ef8-c406-982a-776f-b80d0f4d2b70.png">



# 環境
- PHP 7.3
- Laravel 5.8

# ペジネーションビューの作成
まずlaravelで以下のコマンドを叩くと自動で`resources/views/vendor/pagination`ディレクトリを作成して、さらにいくつかのデフォルトなどで使用されるペジネーションビューを設置してくれます。

```shell:vendor：publishコマンド
php artisan vendor:publish --tag=laravel-pagination
```

ペジネータインスタンスの`links()`メソッドでは、先ほど作成されたディレクトリ下にある`bootstrap-4.blade.php`がデフォルトのペジネーションビューに設定されています。

ちょっとだけペジネーションのHTMLをいじりたいならこれを直接編集してもいいですが、今回は一から作り直したいので`pagination`ディレクトリ下に新しくペジネーションビューを作成します。

```shell:
resources
   ├ ~
   └ views
       └ vendor
            └pagination
                  ├bootstrap-4.blade.php
                  ├ ~
                  └ original_pagination_view.blade.php <-新しくファイルを作成
```
## ペジネーションビューの雛形
まずはペジネーションビューの雛形を作成します。

```html:original_pagination_view.blade.php
@if ($paginator->hasPages())
    <ul class="pagination" role="navigation">
        {{-- First Page Link --}}
            <!-- 最初のページへのリンク -->

        {{-- Previous Page Link --}}
            <!-- 前のページへのリンク -->

        {{-- Pagination Elemnts --}}
            {{-- Array Of Links --}}
                <!-- それぞれのページへのリンク -->

        {{-- Next Page Link --}}
            <!-- 次のページへのリンク -->

        {{-- Last Page Link --}}
            <!-- 最後のページへのリンク -->

    </ul>
@endif
```
基本的にペジネーションビューは`$paginator->xxx()`といった形のペジネータインスタンスメソッドを使用して作成していきます。
`hasPages()`はクエリビルダやEloquent queryで`paginate()`メソッドなどが使用された時に与えられたアイテムの数と引数を比較して引数よりも多かった場合Trueを返します。たぶん。

あとは好みで`<nav>`などをつけたり`class`を書き加えたりしましょう。
今後はこの雛形に沿って各機能を実装していきます。


## First Page Linkの作成

```html:original_pagination_view.blade.php
{{-- First Page View --}}
    <li class="page-item {{ $paginator->onFirstPage() ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->url(1) }}">&laquo;</a>
    </li>
```
`onFirstPage()`はペジネータが最初のページを扱っていればTrueを返し、そうでないならFalseを返します。
`url()`は()内で指定したページのURLを取得します。

現在のページが最初のページの時には`class`に`disabled`を追加することでリンクを無効化しています。


## Previous Page Linkの作成

```html:original_pagination_view.blade.php
{{-- Previous Page Link --}}
    <li class="page-item {{ $paginator->onFirstPage() ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->previousPageUrl() }}">&lsaquo;</a>
    </li>
```
First Page Linkとほとんど一緒ですね。
`previousPageUrl()`は現在のページから一つ前のページのURLを取得しています。


## Array Of Linksの作成
リンク数の管理を行う際、直接ペジネーションビュー内に変数を作成してもいいのですが、いちいち変更するのが面倒なので定数を作成しましょう。
`config`ディレクトリ下に`const.php`を作成して次のように記述しましょう。

```php:const.php
<?php    
    //paginate
    'PAGINATE' => [
        'LINK_NUM' => '5', //paginationの一度に表示するリンクの数
    ],
```
これで簡単にページのリンクを表示する数を管理・変更できるようになりました。

次はこれを使って各ページへのリンクを表示するコードを書いていきます。


```php:original_pagination_view.blade.php
{{-- Pagination Elemnts --}}
    {{-- Array Of Links --}}
    {{-- 定数よりもページ数が多い時 --}}
    @if ($paginator->lastPage() > config('const.PAGINATE.LINK_NUM'))

        {{-- 現在ページが表示するリンクの中心位置よりも左の時 --}}
        @if ($paginator->currentPage() <= floor(config('const.PAGINATE.LINK_NUM') / 2))
            <?php $start_page = 1; //最初のページ ?> 
            <?php $end_page = config('const.PAGINATE.LINK_NUM'); ?>
 
        {{-- 現在ページが表示するリンクの中心位置よりも右の時 --}}
        @elseif ($paginator->currentPage() > $paginator->lastPage() - floor(config('const.PAGINATE.LINK_NUM') / 2))
            <?php $start_page = $paginator->lastPage() - (config('const.PAGINATE.LINK_NUM') - 1); ?>
            <?php $end_page = $paginator->lastPage(); ?>

        {{-- 現在ページが表示するリンクの中心位置の時 --}}
        @else
            <?php $start_page = $paginator->currentPage() - (floor((config('const.PAGINATE.LINK_NUM') % 2 == 0 ? config('const.PAGINATE.LINK_NUM') - 1 : config('const.PAGINATE.LINK_NUM'))  / 2)); ?>
            <?php $end_page = $paginator->currentPage() + floor(config('const.PAGINATE.LINK_NUM') / 2); ?>
        @endif

    {{-- 定数よりもページ数が少ない時 --}}
    @else
        <?php $start_page = 1; ?>
        <?php $end_page = $paginator->lastPage(); ?>
    @endif

    {{-- 処理部分 --}}
    @for ($i = $start_page; $i <= $end_page; $i++)
        @if ($i == $paginator->currentPage())
            <li class="page-item active"><span class="page-link">{{ $i }}</span></li>
        @else
            <li class="page-item"><a class="page-link" href="{{ $paginator->url($i) }}">{{ $i }}</a></li>
        @endif
    @endfor
```

`config('const.PAGINATE.LINK_NUM')`で`const.php`に書いた`LINK_NUM`を呼び出しています。
また`lastPage()`は最後のページ数を、`currentPage()`は現在のページ数を、`url($page)`は入力されたページ数のURLをそれぞれ返します。

だいぶ長くなってしまいましたが、難しいことは書いてないので何をやっているかは理解できるかと思います。
強いて言えば定数を`$link_num`などの変数に入れて使用したり、`$elements`などを用いて書き換えるとコードがより読みやすくなるかもしれません。


## Next Page LinkとLast Page Link
FirstやPreviousとほぼ一緒なのでさらっと書いていきましょう。

```html:original_pagination_view.blade.php
{{-- Next Page Link --}}
    <li class="page-item {{ $paginator->currentPage() == $paginator->lastPage() ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->nextPageUrl() }}">&rsaquo;</a>
    </li>

{{-- Last Page Link --}}
    <li class="page-item {{ $paginator->currentPage() == $paginator->lastPage() ? ' disabled' : '' }}">
        <a class="page-link" href="{{ $paginator->url($paginator->lastPage()) }}">&raquo;</a>
    </li>
```

`nextPageUrl()`は次のページのURLを出力します。

これでオリジナルのペジネーションビューが完成しました！

# オリジナルのペジネーションビューを反映させる
ペジネーションビューが完成しましたが、デフォルトのペジネーションビューには`bootstrap-4.blade.php`が指定してあります。
なので`original_pagination_view.blade.php`が使用されるように変更しましょう。

デフォルト以外のペジネーションビューを使用する場合は、`links()`メソッドをしようする際に第一引数にビュー名を指定するか、`AppServiceProvider`でデフォルトとして設定します。


## links()で引数に指定する方法
こちらの方法ではいつも使っている`links()`メソッドで引数に使いたいペジネーションビューを指定するだけで使用することができます。

```html:index.blade.php
{{ $items->links('original_pagination_view.blade.php') }}
```

## AppServiceProviderで設定する方法
オリジナルのペジネーションビューを全てのページのデフォルトとして使用したい場合、`app/Providers`にある`AppServiceProvider`を編集します。

```php:AppServiceProvider.php
use Illuminate\Pagination\Paginator; //<-追加する

-------------------------

public function boot()
{
    Paginator::defaultView('original_pagination_view.blade.php');
}

```

これでオリジナルのペジネーションビューがデフォルトとして使用されるようになりました。

もしシンプルペジネーションのビューを適用したい場合は`defaultSimpleView()`を使用してください。


# 最後に
Array Of Linksがもうちょっとスマートに書ける…かもしれないけど、いい方法が思いつかなかったので未来の自分に丸投げします。
あとは`$elements`についてあまり理解できてないので、機会があればそれを利用したペジネーションビューを作ってみたいと思います。

### 参考文献
* [Laravel 5.8 データベース：ペジネーション](https://readouble.com/laravel/5.8/ja/pagination.html)
* [Laravel のページャー（ページネーション）の表示とカスタマイズ](https://pgmemo.tokyo/data/archives/1278.html)