【Rails】コントローラーにたくさんコードを書いてしまった・・・そんな時はモデルに処理を寄せよう！

どーもへっぽこです。
コントローラーって便利ですよね、ルーティングしてアクション呼び出すだけでその中の処理を走らせることができる。
ついつい便利で・・
```ruby
def A
  a = 3
  b = 2
  c = a + b
  
  return c
end 
def B
  a = 4
  b = 2
  c = a + b
  
  return c
end
def C
  a = 7
  b = 6
  c = a + b
  return c
end
def D
  a = 1
  b = 9
  c = a + b
  
  return c
end
def E
  a = 6
  b = 2
  c = a + b
  
  return c
end
...
```
たくさんコードを書いてしまう。。
しかし、こんなコントローラーのことをなんと呼ぶか知ってますか？
![topics_46_4.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007139/7a3b9127-9a04-c0bf-c124-fe5f06e8c8c6.jpeg)
<font color="Red" >__ファットコントローラーです！__</font>
たくさんのコードで肥やせてしまったコントローラーのことを指すんですね。
さてクイズです、ファットコントローラーは悪いでしょうかそうでないでしょうか？
答えは・・
![1182533.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007139/0ba7d3cd-2c6c-a444-c3de-f99fb8daa9cc.png)
です！
え、なんで。。
そう思った方！コントローラーの役割を考えてみましょう。
図で表すとこちらです。
![スクリーンショット_2022-06-15_17_21_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007139/55ad19e1-d416-b1b0-6694-1be58e2f29a1.png)
[出典：エンジニアファースト](https://engineer-first.net/fat-controller-workarounds)
見てわかる通り、コントローラーはブラウザーからリクエスト(身近なものでいくとURLでアクセスする)を受け取って、モデルでデータもらって、ビューにそれを渡して、ブラウザにレスポンスを返すのが役割ですね。
つまりコントローラーは・・・
<font color="Red" >__リクエストを受け取って返すだけのために存在しているのです！！！__</font>
図でわかる通り、データのやりとりはモデルがしています。
お兄さん、こんなコードをコントローラーで書いたことありませんか？
```ruby
def index
  user = User.all
  return user
end
```
でも図ではこうなってるでしょ？
![スクリーンショット_2022-06-15_17_21_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007139/f39a657a-1904-9fe8-1b05-f1800a6e11d6.png)
でも↓のコードは
```ruby
def index
  user = User.all
  return user
end
```
実際コントローラーがモデルの役割を担ってしまってる。
だって、`User.all`でデータベースにアクセスしてデータ取ってきてるじゃん。
さて寄り道も終わり。
つまりなんでファットコントローラーになってしまうかというと
__『本来モデルに書かないといけないところをコントローラーに書いている』__
これに尽きます。
じゃあどうすればいいの？
答えは超簡単、**モデルに書けばいいだけですね。**
以下のようにしてコントローラーからモデルに書いた処理を呼び出せます。
```ruby
#コントローラー
def index
  user = User.get_user
  return user
end
```
```ruby
#モデル
def self.get_user
  user = User.all
  return user
end
```
このコードはさっき出てきた↓のコードと同義です。
```ruby
def index
  user = User.all
  return user
end
```
簡単に説明すると、モデルで `def self.○○` で定義しておくとコントローラーで `モデル名.○○`という形でモデル内で定義したメソッドを呼び出せます。
`self`の部分がモデル名にあたるので、`User`というモデル名だったら`User.○○`、`Project`というモデル名だったら`Project.○○`という形になりますね。
`○○`の部分はメソッド名、今回は`get_user`というメソッド名だったので、`User.get_user`で
```ruby
  user = User.all
  return user
```
↑の処理を呼び出せましたね。
賢いお兄さんならわかったかな？
はいそうです、コントローラーの中身はほとんど書く必要がありません。
ほとんど
```ruby
def index
  #モデルの処理を呼び出す
return 〇〇
end
```
という形で終わらせることができます。
これを守っていればファットコントローラーになりようがありませんね。
# ちょっと発展
モデルに処理を寄せる時、なんでもかんでも処理をまとめることは危険です。
重要なのは
__共通化を意識することです。__
例えばこんなコードがコントローラーに書かれていたとします。
```ruby
def projects_data
  rewards = Reward.all #ドロップダウンで使用するデータ
  locations = Location.all #ドロップダウンで使用するデータ
  stations = Station.all #ドロップダウンで使用するデータ
  terms = Term.all #ドロップダウンで使用するデータ
  skills = Skill.all #トランスファーリストで使用するデータ
  os = OperatingSystem.all #トランスファーリストで使用するデータ
  db = Database.all #トランスファーリストで使用するデータ
  position = Position.all #トランスファーリストで使用するデータ
  characteristic = Characteristic.all #トランスファーリストで使用するデータ
  tool = Tool.all #トランスファーリストで使用するデータ
end
```
ドロップダウンは↓で
![スクリーンショット 2022-06-15 18.05.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007139/cf486fbc-27d0-0a37-1ccc-8a1f95f168a2.png)
トランスファーリストは↓
![スクリーンショット 2022-06-15 18.06.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007139/2838bee2-5901-931b-9e7d-c00e2610ca99.png)
です。
つまり、コメントが書かれたデータはそこでしか使われないという意味です。
このコードをモデルに以下のようにまとめたとします。
```ruby
def self.get_projects_data
  rewards = Reward.all
  locations = Location.all
  stations = Station.all
  terms = Term.all
  skills = Skill.all  
  os = OperatingSystem.all
  db = Database.all
  position = Position.all
  characteristic = Characteristic.all
  tool = Tool.all
    
  return rewards,locations,stations,terms,skills,os,db,position,characteristic,tool
end
```
見ての通り、これだと`projects_data`のアクションでしか呼び出せない汎用性のないメソッドとなってしまいます。
そこでモデルを以下のように変更します。
```ruby
def self.drop_down
  rewards = Reward.all
  locations = Location.all
  stations = Station.all
  terms = Term.all
  return rewards, locations, stations, terms
end
def self.transferlist
  skills = Skill.all
  os = OperatingSystem.all
  db = Database.all
  position = Position.all
  characteristic = Characteristic.all
  tool = Tool.all
  return [
    skills,
    os,
    db,
    position,
    characteristic,
    tool
  ]
end
```
これであれば`projects_data`以外のアクションでも、ドロップダウンとトランスファーリストを使いたいアクションで個別に呼び出すことができますね。
とても汎用的です。
つまり、処理はモデルに寄せつつその処理は細かく分散していった方が汎用的になりやすいということです。
なので、モデルに処理を寄せる時は**共通化**する意識を持ちましょう！
以上、へっぽこでした。