[Rails] パンくずリストで多階層のカテゴリーを表示する

# 概要
ancestryで作った多階層カテゴリーを、パンくずリストに表示させます。
パンくずリストの作成にはgretelというgemを使用しました。
今回はECサイトで見られるような、商品のカテゴリーを表示するパンくずリストを作成していきます。
<img width="821" alt="パンくず カテゴリ.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619967/04d8a1af-7b76-40a5-309b-c2b944001a44.png">

# 前提
- ancestryを使ってカテゴリーテーブルを作成している。
- カテゴリー一覧ページ、カテゴリー別商品一覧ページ（各パンくずのリンク先のページ）を作成している。

今回解説するパンくずリストの構成は以下のようになります。
トップページ > カテゴリー一覧 > 親カテゴリー > 子カテゴリー > 孫カテゴリー

# 参考記事
多階層カテゴリーの作成にはancestryというgemを使用しています。
導入方法については以下の記事が参考になります。
https://qiita.com/Sotq_17/items/120256209993fb05ebac
https://qiita.com/pdm21/items/fe0055b3190af790f1c0

また、カテゴリー別商品一覧ページの作成については、私の過去の投稿を参考にして頂ければと思います。
https://qiita.com/Iwa_tech/items/8c396723bd9ea8e9894f

# 実装
## 導入
### gemのインストール
まずはgretelをインストールします。

```rb
# Gemfile
gem 'gretel'

# ターミナル
$ bundle install
```

### 設定ファイルの生成

ターミナルに以下のように打ち込み、実行します。

```rb
$ rails generate gretel:install
```

すると、configディレクトリ下に、breadcrumbs.rb というファイルが自動生成されます。
このファイル内に、どのパンくずをどのページにリンクさせるかの設定を記述していくことになります。

```config/breadcrumbs.rb
crumb :root do
  link "Home", root_path
end

# crumb :projects do
#   link "Projects", projects_path
# end

# crumb :project do |project|
#   link project.name, project_path(project)
#   parent :projects
# end

# crumb :project_issues do |project|
#   link "Issues", project_issues_path(project)
#   parent :project, project
# end

# crumb :issue do |issue|
#   link issue.title, issue_path(issue)
#   parent :project_issues, issue.project
# end

# If you want to split your breadcrumbs configuration over multiple files, you
# can create a folder named `config/breadcrumbs` and put your configuration
# files there. All *.rb files (e.g. `frontend.rb` or `products.rb`) in that
# folder are loaded and reloaded automatically when you change them, just like
# this file (`config/breadcrumbs.rb`).
```

## カテゴリー一覧ページのパンくず作成
まずは簡単に実装できる、トップページ > カテゴリー一覧 の部分のパンくずからつくります。

### パンくずの定義
breadcrumbs.rb ファイル内に、パンくずのリンク先を記述します。
link_toメソッドと同じ要領で、リンクに表示する文字、リンク先のパスを指定します。

```config/breadcrumbs.rb
crumb :root do
  link "トップページ", root_path
end

crumb :category_index do
  link "カテゴリー一覧", categories_path
end
```

### viewに設置
#### 部分テンプレート作成
複数のページから呼び出せるよう、パンくずリストの部分テンプレートを作成します。
layouts/application.html に記述してもいいですが、今回はページ毎に表示の有無を分けたかったため、部分テンプレートを使うことにしました。
separator属性には、パンくず間の区切りの表示を定義できます。

```_breadcrumbs.html.haml
.breadcrumbs
  = breadcrumbs separator: " &rsaquo; ", class: "breadcrumbs-list"
```

#### 部分テンプレート呼び出し
カテゴリー一覧ページのviewにて、パンくずリストを呼び出します。
下記の１行目の記述で呼び出すパンくずを指定しています。

```app/views/categories/index.html.haml
- breadcrumb :category_index
= render "breadcrumbs"
```

ここまでで、カテゴリー一覧ページにパンくずリストを表示することが出来たかと思います。
ちなみに、表示しているページのパンくずには自動で current というclassが付与されるため、CSSを使って以下のように表示しているページ名を強調したりも出来ます。
<img width="647" alt="パンくず１.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619967/aac910de-df45-8616-94f8-ee8dd5dbd64c.png">

## カテゴリー詳細ページのパンくず作成
続いて、カテゴリー別商品一覧ページ上にパンくずリストを表示させます。
以下のように、先ほど作ったパンくずの下に続けて各カテゴリーのパンくずを追加することを目指します。

トップページ > カテゴリー一覧 > 親カテゴリー > 子カテゴリー > 孫カテゴリー

### パンくずの定義
親・子・孫の3つのカテゴリーに対してパンくずを設定します。
crumb の定義部分で do |変数名| とすることで、パンくず内で変数を使えるようになります。

```config/breadcrumbs.rb
# 親カテゴリーのパンくず
crumb :parent_category do |category|
  category = Category.find(params[:id]).root
  link "#{category.name}", category_path(category)
  parent :category_index
end
# -----------------------------------------------------------------
# 子カテゴリーのパンくず
crumb :child_category do |category|
  category = Category.find(params[:id])
  # 表示しているページが子カテゴリーの一覧ページの場合
  if category.has_children?
    link "#{category.name}", category_path(category)
    parent :parent_category

  # 表示しているページが孫カテゴリーの一覧ページの場合
  else
    link "#{category.parent.name}", category_path(category.parent)
    parent :parent_category
  end
end
# -----------------------------------------------------------------
# 孫カテゴリーのパンくず
crumb :grandchild_category do |category|
  category = Category.find(params[:id])
  link "#{category.name}", category_path(category)
  parent :child_category
end
```
parent: の後には、そのパンくずの親に設定したいcrumb名を指定します。
（カテゴリー一覧ページのように、parentの指定をしなかった場合は、rootのパンくずが親となります）

子カテゴリーのパンくずだけ処理に少し工夫が必要です。
子カテゴリーのパンくずを孫カテゴリーのページから呼び出した場合と、子カテゴリーのページから呼び出した場合の2つの条件で処理を変えるようにします。

### viewに設置
最後にviewでパンくずを呼び出せば完成です。
ここでの@category には、コントローラー側の記述で、詳細表示させるカテゴリーを代入しています。
@categoryがどの階層かによって、表示させるパンくずを分けます。
呼び出したパンくずに親が設定されている場合は、親のパンくずも一緒に呼び出してくれます。

```app/views/categories/show.html.haml
- if @category.root?
  - breadcrumb :parent_category
- elsif @category.has_children?
  - breadcrumb :child_category
- else
  - breadcrumb :grandchild_category
= render "breadcrumbs"
```

以上で、カテゴリー詳細ページにパンくずリストを表示させることが出来ました。
選択したカテゴリーによって、パンくずの階層やカテゴリー名が適切に変化するか確認しましょう。
<img width="750" alt="パンくず２.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619967/6da9cbcb-46aa-780d-17e0-cf4830ebbc22.png">

# 最後に
今回はancestryで作成した多階層カテゴリーを、パンくずリストに表示させる方法を解説しました。
ここまでご覧いただき、ありがとうございました。

パンくず機能作成に当たって参考にさせて頂いた記事です
https://qiita.com/you8/items/d2d37a745060b79c112f
https://qiita.com/Azure0701/items/16de34a0010eb7f05d89