#はじめに
多対多の関係のアソシエーション(いいね機能)を実装してみていまいち理解できていないところについて本記事で振り返っていこうと思います!
具体的な実装方法については以下の記事がすごくわかりやすかったです。
【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸
#アソシエーションとは
Railsでは、「関連付け(アソシエーション: association)」とは2つのActive Recordモデル同士のつながりを指します。
[Railsガイド]
Railsガイドにも記載してあるように2つのモデル同士の繋がりのことを指している。
アソシエーションを組むことでのメリットとしては、モデル同士を関連づけるためのコードがシンプルな記述で済むということ。
#DB設計で重要なこと
多対多に関わらず、DBを設計する際は、以下の項目について考える必要がある。
####1,どのような情報か
ユーザーに関する情報ならuser、投稿に関する情報ならpostなどどんな情報なのかを洗い出す。
####2,情報の詳細
具体的にどのような情報を持つべきなのか。
userであればnameやemailなどさらに属性についても考える必要ある。
name→string,email→string etc..
####3,関係(リレーション)
意味合い的にはアソシエーションと同じで、情報同士がどのように結びついているか。
####4,関係の多重度
お互いの情報がどのようなデータ数を扱うか。
実際に上記を意識して作成したER図がこちら。
この線の部分がリレーションになり、鳥の足みたいなものが多重度を示している。詳しいIE記法に関しては以下記事参照。
ER 図の Crow's Foot 記法 (IE 記法)
####[補足]中間テーブルとは
今回のER図で言うLikeのことを指す。
お互いの情報をつなぎ合わせているテーブルと考えるとイメージしやすいかも。
#実装
ここからは実装方法についてまとめていきます。
ルーティング、ビュー、コントローラについて1つずつ確認していきます。
##多対多のルーティング設定
ここでポイントになってくるのが、「has_many」「belong_to」「ネストさせる」についてです。
####has_many
has_many関連付けは、他のモデルとの間に「1対多」のつながりがあることを示します。
[Railsガイド]
今回の場合、user:1対 多: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(親)を記載する。
※1つのいいねに対して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に対応した7つのアクションを生成する。
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ガイド