多対多のアソシエーションの仕組みと実装について（いいね機能）

#はじめに
多対多の関係のアソシエーション（いいね機能）を実装してみていまいち理解できていないところについて本記事で振り返っていこうと思います！
具体的な実装方法については以下の記事がすごくわかりやすかったです。
[【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸](https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1#%E3%81%8A%E6%B0%97%E3%81%AB%E5%85%A5%E3%82%8A%E3%83%9C%E3%82%BF%E3%83%B3%E3%81%AE%E5%AE%9F%E8%A3%85)
#アソシエーションとは
> Railsでは、「関連付け(アソシエーション: association)」とは2つのActive Recordモデル同士のつながりを指します。
[Railsガイド]

Railsガイドにも記載してあるように２つのモデル同士の繋がりのことを指している。
アソシエーションを組むことでのメリットとしては、モデル同士を関連づけるためのコードがシンプルな記述で済むということ。


#DB設計で重要なこと
多対多に関わらず、DBを設計する際は、以下の項目について考える必要がある。
####1,どのような情報か
ユーザーに関する情報ならuser、投稿に関する情報ならpostなどどんな情報なのかを洗い出す。
####2,情報の詳細
具体的にどのような情報を持つべきなのか。
userであればnameやemailなどさらに属性についても考える必要ある。
name→string,email→string etc..
####3,関係（リレーション）
意味合い的にはアソシエーションと同じで、情報同士がどのように結びついているか。
####4,関係の多重度
お互いの情報がどのようなデータ数を扱うか。
実際に上記を意識して作成したER図がこちら。
この線の部分がリレーションになり、鳥の足みたいなものが多重度を示している。詳しいIE記法に関しては以下記事参照。
[ER 図の Crow's Foot 記法 (IE 記法)](https://knooto.info/erd-crows-foot/)
![スクリーンショット 2021-04-22 0.57.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613784/cdaa992d-da8c-b777-327a-2286ff596d16.png)

####[補足]中間テーブルとは
今回のER図で言うLikeのことを指す。
お互いの情報をつなぎ合わせているテーブルと考えるとイメージしやすいかも。

#実装
ここからは実装方法についてまとめていきます。
ルーティング、ビュー、コントローラについて１つずつ確認していきます。

##多対多のルーティング設定
ここでポイントになってくるのが、「has_many」「belong_to」「ネストさせる」についてです。
####has_many
>has_many関連付けは、他のモデルとの間に「1対多」のつながりがあることを示します。
[Railsガイド]

今回の場合、user:１対 多:likeのような関係になるので。

```
class User < ApplicationRecord
  has_many :likes
end
```
user(親)のモデルにlike(子)を記載する
※likeは複数(多)になるので複数形で記述すること。
####belong_to
>あるモデルでbelongs_to関連付けを行なうと、他方のモデルとの間に「1対1」のつながりが設定されます。
[Railsガイド]

今回のようにhas_manyが使われている場合、反対側のモデルで用いられる。
なので

```
class Like < ApplicationRecord
    belongs_to :user
    belongs_to :study
end
```
has_manyとは逆で、like(子)のモデルにuser(親)study(親)を記載する。
※１つのいいねに対してuser,studyは一つなので単数形で記述。
####ネストさせる
結論このように記述する。

```
 resources :study, only: [:new, :edit] do
      resources :like ,only: [:create, :destroy]
 end
```
ネストした時のメリットは以下のrails routesの結果でわかる。
・ネストしない場合のrouting

```
POST   /like(.:format)                                                                                  
```
・ネストした場合のrouting

```
POST /study/:study_id/like(.:format) 
```
studyにネストすることで簡単にstudy_idを取得することができる。
※このように簡単に取り出すためには、likeの属性を設定するときに「親モデル名_id」とする必要がある。
####[補足]resourceとresourcesの違い
resource → CRUDに対応した７つのアクションを生成する。
resources　→indexとidつきパスが生成されない

##ビュー
「いいねする」と「いいね解除」二つの記述が必要になる。
表示を切り替えるためには「いいねされているか？」を検証する必要がある。
###いいねされているか？を判断するメソッド

```
class Study < ApplicationRecord
  has_many :likes
  def liked_by?(user)
    likes.where(user_id: user.id).exists?
  end
end
```

```
likes.where(user_id: user.id).exists?
```
likesテーブルのuser_idが存在している(exists?)かどうか。

###いいねボタンを表示

```
 <% if study.liked_by?(@current_user) %> 
     <%=link_to study_like_path(study.id), method: :delete,class:"like_btn" do %>
      <i class="far fa-thumbs-up liked"></i>
      <span><%= study.likes.count %></span>
   <% end %>
<% else %>
<%=link_to "/study/#{study.id}/like", method: :post ,class:"like_btn" do%>
  <i class="fas fa-thumbs-up"></i>
   <span><%= study.likes.count %></span>
  <% end %>
<% end %>
```

if文の中身のみ解説させていただきます。

```
if study.liked_by?(@current_user) 
```
study(投稿)のlikesテーブルのuser_idがログインユーザーの場合
※存在するということは、いいねされているということ

```
<%=link_to study_like_path(study.id), method: :delete,class:"like_btn" do %>
      <i class="far fa-thumbs-up liked"></i>
      <span><%= study.likes.count %></span>
<% end %>
```
削除へのリンクを記述。

###コントローラ

```
def create
    like = @current_user.likes.build(study_id: params[:study_id])
    like.save
    study = Study.find_by(id: params[:study_id])
    redirect_to("/user/road/#{study.user_id}")
 end

 def destroy
    like = Like.find_by(study_id: params[:study_id],user_id: @current_user.id)
    like.delete
    study = Study.find_by(id: params[:study_id])
    redirect_to("/user/road/#{study.user_id}")
 end
```
作成(create)するためのコード

```
like = @current_user.likes.build(study_id: params[:study_id])
```
likesのstudy_idにstudy(投稿)のid
@current_user.likesとすることでlikeのuser_idに@current_user.idが生成される。
削除(destory)するためのコード

```
like = Like.find_by(study_id: params[:study_id],user_id: @current_user.id)
like.delete
```
likeの各値を取得し、削除する。

#まとめ
・belong_toやhas_manyを使用することで多対多の関係も簡単に実装することができる。
・アソシエーションの組み方について理解が深まった。

#参考資料
[Railsガイド](https://railsguides.jp/)