Rails:パンくずリストに2ページ目以降に｢Xページ｣と表示する方法

ページネーション機能がある一覧表示ページのパンくずリストに、｢Xページ目｣と表示するための実装方法を紹介します。

テキストだとちょっと伝わりづらいですが、

- 1ページ目ならば`ページ名`
  <img width="167" alt="ScreenShot 2020-06-28 15.19.19.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331354/188e625f-5170-3683-33a3-63606728bf5d.jpeg">

- 2ページ目ならば`ページ名 > #{ページ数}ページ目`
  <img width="260" alt="ScreenShot 2020-06-28 15.16.04.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331354/78336e1f-a623-a5ba-abf5-c22aef90abc5.jpeg">

のように、｢1ページ目と2ページ目以降でことなる表示｣にし、｢2ページ目以降にはページ数を表示する｣、ということを今回の仕様とします。


## 環境
自作のプロテイン口コミサービスに導入( https://github.com/yuki0920/supplebox )のアイテム一覧ページ(/products/index)に導入すること前提に説明します。

また、ページネーション機能は`kaminari`で、パンくずリスト機能は`gretel`ですでに実装されていることを想定しています。

```
# Gemfile
gem 'kaminari'
gem 'gretel'
```

## 現状
1ページ目でも2ページ目以降でもページ名(アイテム一覧)が表示されています。

<img width="167" alt="ScreenShot 2020-06-28 15.19.19.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331354/188e625f-5170-3683-33a3-63606728bf5d.jpeg">


gretelのオーソドックスな使い方をしています。

```config/breadcrumbs.rb
crumb :products do
  link 'アイテム一覧', products_path
  parent :root
end
```

```app/views/products/index.html.haml
- breadcrumb :products
```

## Xページ目を表示する

設定ファイルに追記します。parentにproductsを設定します。ページ数をpageとして第2引数で受け取り、
pageを使ってページ数を表示するようにします。

第2引数で受け取った値を表示できる、というのは[READMEのサンプル集](https://github.com/WilHall/gretel#more-examples)に載っています。

```config/breadcrumbs.rb
crumb :products do
  link 'アイテム一覧', products_path
  parent :root
end

# 追記
crumb :products_pagination do |page|
  link "#{page}ページ目"
  parent :products
end
```

```app/views/products/index.html.haml
- breadcrumb :products_pagination params[:page]
```

呼び出し元のViewテンプレートでは、breadcrumbメソッドの引数に注目です。

- 第1引数に、products_pagenation
- 第2引数に、params[:page]

kaminariでは、ページ数がクエリパラメータとして渡ってきます(例えば、`/products?page=2`)ので、`params[:page]`でページ数を取得して第2引数として渡すのです。

このようにすることで、1ページ目も2ページ目以降もパンくずリストにページ数を表示することができました。

が、1ページ目のときには問題があります。

`params[:page]`がnilのため、`Home › アイテム一覧 › ページ目`の表示になってしまいます。

## 1ページ目と2ページ目以降で場合分けをする

ということで、`param[:page]`の値を使って､1ページ目と2ページ目以降で場合分けをします。

```app/views/products/index.html.haml
- if params[:page].nil? || params[:page] == 1
  = breadcrumb :products
- else
  = breadcrumb :products_pagination, params[:page]
```

これで、当初の目標を満たすことができました。

- 1ページ目ならば`ページ名`
- 2ページ目ならば`ページ名 > #{ページ数}ページ目`

とはいえ、Viewファイルがごちゃっとしているのが気になります。

## リファクタリング

ロジックをViewファイルに書くと見通しが悪くなってしまうので､メソッド化してヘルパーに切り出します。

```app/helpers/application_helper.rb
def breadcrumb_pagination
  if params[:page].nil? || params[:page] == 1
    breadcrumb :products
  else
    breadcrumb :products_pagination, params[:page]
  end
end
```

呼び出し元もヘルパーに定義したメソッドに修正します。
以上で完了です。

```app/views/products/index.html.haml
- breadcrumb_pagination
```

## 補足(System Spec)

パンくずリストの実装に関して下記のようなテストを書いて仕様を担保しました。
パンくずリストに関わる実装のみを抜粋しているので、雰囲気だけでも伝えられれば。

やはり、テストを書くとリファクタリングがはかどりますね!

```spec/system/products_spec.rb
# frozen_string_literal: true

require 'rails_helper'

describe 'アイテム', type: :system do
  describe '一覧機能' do
    before do
      create_list(:product, 13)
    end

    it 'ユーザーはアイテム一覧を閲覧できること' do
      visit products_path
      expect(page).to have_content 'Home › アイテム一覧'
      expect(page).to_not have_content 'Home › アイテム一覧 › 1ページ目'
      expect(page).to have_selector '.pagination'
      within '.pagination' do
        click_link '2'
      end
      expect(page).to have_content 'Home › アイテム一覧 › 2ページ目'
      within '.breadcrumbs' do
        expect(page).to have_link 'アイテム一覧' ,href: products_path
      end
    end
  end
end
```