【Rails5】ドロップダウンリスト（ハンバーガーメニュー）の実装（コントローラー編）メルカリクローンサイトの作成

#背景
カリキュラムの過程でご存知メルカリのクローンサイトを作りました。
その際に、ヘッダーメニューにあるドロップダウンリスト（ハンバーガーメニュー？）を実装したのでその記録です。

仕上がりはこんな感じです。
[![Image from Gyazo](https://i.gyazo.com/4982d9600afc79ea6ada81f450302960.gif)](https://gyazo.com/4982d9600afc79ea6ada81f450302960)

css編を書きました。
こちらです。
[【Rails5】ドロップダウンリストの実装（haml,scss編）〜メルカリクローンサイトの作成](https://qiita.com/pharma_tech3/items/3a1ddbd7cddc3c5f14f7)


#リストに呼び出したいDB構造など
今回、メルカリのサイトをクローンするにあたり、カテゴリーは、単一テーブル継承にすることで実装しています。

例えば、以下のような構造をしています。

| id  |       category | parent_id   |
|:----|:--------------:|:-----------:|
| 1   |        レディース |             |
| 2   |           メンズ |             |
| 13   |          トップス |   1       |
| 14   |         ジャケット |   2       |
| 115   |          Tシャツ |    13    |
| 116   |    ダウンジャケット |     14   |

この例だと、レディース>トップス>Tシャツ、　メンズ>ジャケット>ダウンジャケットという階層になります。
ちなみにビューで値を呼び出すときは、@category.childrenや@category.parentで子要素や親要素を呼び出すことが可能。


#ドロップダウンリストの要素が多すぎ
さて、実装に入りましょう。
色々な記事を参考にさせていただくと、ドロップダウンはCSSだけでいけることが判明。~~（後日別記事書きます）~~→[こちら](https://qiita.com/pharma_tech3/items/3a1ddbd7cddc3c5f14f7)

じゃぁhtmlでカテゴリー名を全部入れればOKでしょ？

いや、待て待て。

カテゴリー名何個あんのよ
[![Image from Gyazo](https://i.gyazo.com/c5f625ebffb53e74d4b4cc9d886c30c5.png)](https://gyazo.com/c5f625ebffb53e74d4b4cc9d886c30c5)

親、子、孫合わせて
**1,325!!**

うん、多いね。

クラス名とかも一緒に書くから全部書くと4,000行の大作になっちゃうじゃない。

スマートじゃないね。
これ地道にやるならプログラミングやってる意味ないじゃん。

DBにデータはあるんだし、なんとかそこから引っ張ってこれないんか？？

#リストの数が多いのでeachを使う
じゃぁ該当するカテゴリー名を変数に入れてrenderをeachさせればいいんじゃね？

まずは親カテゴリ。
親カテゴリはDBから呼び出さずにビューに記載。そして子カテゴリの呼び出し(@category_children1)

```views/product/_header.html.haml

 %ul.header__menu-box--left__parents
    %li.header__menu-box--left__parents__parent
      = link_to "レディース", category_path(id: 1)
      %ul.header__menu-box--left__children
         = render partial: "header_child", collection: @category_children1, as: "category"
```
続いて子カテゴリ。そして孫カテゴリーの呼び出し(category.children)

```views/product/_header_child.html.haml
%li.header__menu-box--left__children__child
  = link_to "#{category.category}", category_path(id: category.id)
  %ul.header__menu-box--left__grand-children
    = render partial: "header_grand-child", collection: category.children, as: "children"
```

さらに孫カテゴリ

```views/product/_header_grand-child.html.haml
%li.header__menu-box--left__grand-children__grand-child
  = link_to "#{children.category}", category_path(id: children)
```

また、コントローラー側ではこのような処理がされています。

```products_controller.rb
def set_categories
    @categories = Category.where(params[:id])
    @category_children1 = Category.where(parent_id: 1)
    @category_children2 = Category.where(parent_id: 2)
    @category_children3 = Category.where(parent_id: 3)
    @category_children4 = Category.where(parent_id: 4)
    @category_children5 = Category.where(parent_id: 5)
    @category_children6 = Category.where(parent_id: 6)
    @category_children7 = Category.where(parent_id: 7)
    @category_children8 = Category.where(parent_id: 8)
    @category_children9 = Category.where(parent_id: 9)
    @category_children10 = Category.where(parent_id: 10)
    @category_children11 = Category.where(parent_id: 11)
    @category_children12 = Category.where(parent_id: 12)
    @category_children13 = Category.where(parent_id: 13)
  end

```

よし、早速動かしてみよう！！！

おやおや、、、
確かに動くんだけど、リンクをクリックしたらエラーが。。。
読み込むテンプレートないじゃん！って怒られている
[![Image from Gyazo](https://i.gyazo.com/f775f36be5ff38db35dd2a172f86cd58.png)](https://gyazo.com/f775f36be5ff38db35dd2a172f86cd58)


部分テンプレートはちゃんとあるし、Missingな訳ないんだけどな

あれ、よーく見ると、ビューを読み込もうとしてるのってcategory_controllerじゃん
product_controllerじゃないのね。。。

そうか、部分テンプレートが同じcategoryフォルダにないから読めないのね。
次のように変更しましょ。

```
= render partial: "products/header_child"
```
こうすれば違うフォルダの部分テンプレートも読めるようになるね。

じゃ更新して、、、
[![Image from Gyazo](https://i.gyazo.com/37f4c3eb54f5f2814c6f9fc15ee697e8.gif)](https://gyazo.com/37f4c3eb54f5f2814c6f9fc15ee697e8)
あれれ？　あれれ？
今度は子と孫のカテゴリーが出なくなったぞ。。。



#ページごとで読み込むコントローラーが違う

ページごとに読み込むコントローラーが違うため、それぞれのコントローラーで変数の設定が必要になるのか。。。
全部のコントローラーに上のあれを書くのは流石に可読性が悪くなるし、
DRYの法則に逆らうね

だるいわ〜

なんとか共通化したりできないの？？


#共通のメソッドConsernsにしてしまう
ありました、ちゃんとそういう機能。

参考記事
[Ruby on RailsでConcernsを利用してControllerの処理を共通化する](https://programming-beginner-zeroichi.jp/articles/142)

早速、以下のように書きます。

```controllers/concerns/common_actions.rb
module CommonActions
  
  extend ActiveSupport::Concern

  def set_categories
    @categories = Category.where(params[:id])
    @category_children1 = Category.where(parent_id: 1)
    @category_children2 = Category.where(parent_id: 2)
       #中略
    @category_children12 = Category.where(parent_id: 12)
    @category_children13 = Category.where(parent_id: 13)
  end

end
```

```必要なコントローラー.rb
  include CommonActions
  before_action :set_categories, only: [必要なアクション]

  #最初に読み込む必要があるため、最初に追記します。
```

でもこれ、コントローラー全部に書き込むのもどうかと思うのでこうしてみました。

```application_controller.rb
class ApplicationController < ActionController::Base
  include CommonActions
  before_action :set_categories
　　　#中略
end
```
こうすることで、全部のコントローラーに記述する必要がなくなりました！！

少しはいいの作れたかな〜
もっといい方法があればご教授くださいまし。