#環境
Rails6.1.3
Ruby3.0.1
#同期処理
##①モデルを作成
今回はコメントに対するいいねなので、user_id
とcomment_id
と関連付けます
% rails g model Good user_id:integer comment_id:integer
##②モデルに関係性を記述
user,commentモデル
にそれぞれ以下の記述を追加
dependent: :destroy
でユーザーかコメントが消えたときにいいねも消えるようになります
has_many :goods, dependent: :destroy
belongs_to :user
belongs_to :comment
##③ルーティングの設定
今回は、コメントに対するいいねということなので、ルーティングがネスト化されます
また、いいねは作るか消すかだけなので、onlyも書いておきます
resources :comments do
resources :goods, only: %i[create destroy]
end
##④controllerの編集
create
とdestroy
についてメソッドを書きます
いいねをするのは、current_user
なので、current_user.id
からuser_id
を取得します
class GoodsController < ApplicationController
def create
@good = Good.create(user_id: current_user.id, comment_id: params[:comment_id])
@good.save
end
def destroy
@good = Good.find_by(user_id: current_user.id, comment_id: params[:comment_id])
@good.destroy
end
end
##⑤viewの設定
<% if logged_in? %>
でログインしているか判定
<% if current_user.good_user?(comment.id) %>
で現在のユーザーがいいねをしているかどうかを判定し、create
かdestroy
か分岐
<div class="comment_goods">
<% if logged_in? %>
<% if current_user.good_user?(comment.id) %>
<%= link_to comment_good_path(comment.id, current_user.goods.find_by(comment_id: comment.id).id), method: :delete do %>
<p class="good-button"><i class="far fa-heart like-btn" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= comment.goods.count %></span></p>
<% end %>
<% else %>
<%= link_to comment_goods_path(comment.id), method: :post do %>
<p class="good-button"><i class="fas fa-heart unlike-btn" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= comment.goods.count %></span></p>
<% end %>
<% end %>
<% end %>
</div>
pathの名前は
% rails routes
で確認
comment_goods POST /comments/:comment_id/goods(.:format) goods#create
comment_good DELETE /comments/:comment_id/goods/:id(.:format)
ここから持ってきます
#非同期処理
非同期処理は、Ajax処理とjQueryを利用します。
jQueryの導入方法は、調べたらたくさん出てくるはずなので調べてみてください。
(Railsのバージョンによって方法が変わるので注意!)
##①いいね機能部分を部分テンプレート化
<% if current_user.good_user?(comment.id) %>
<%= link_to comment_goods_path((comment.id)), method: :delete, remote: true do %>
<p id="good_<%= comment.id %>"><i class="fas fa-heart unlike-btn" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= comment.goods.count %></span></p>
<% end %>
<% else %>
<%= link_to comment_goods_path((comment.id)), method: :post, remote: true do %>
<p id="good_<%= comment.id %>"><i class="far fa-heart like-btn" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= comment.goods.count %></span></p>
<% end %>
<% end %>
Ajax処理で更新させたい部分(今回であればいいねのボタン部分)を部分テンプレート化します。
ここで同期処理⑤のViewからの変更点があります。
###remote: true doを追記
remote: true do
にすることで、js形式のリクエストを送信することができます。
create.js.erb
,destroy.js.erb
が発動します。
###ボタンのidを変更
id="good_<%= comment.id %>"
にすることで、更新されるいいねボタンを限定しています。
これを行わないと、全てのいいねボタンが同時に更新されることになってしまいます。
###パスの変更
以下の記事を参考にさせていただきました。
主キーの所得方法で、めちゃくちゃ悩みました、、、
##②レンダリング先での表示方法
レンダリング先で使用している変数(今回であればcomment)を部分テンプレートにも渡すために、
locals:{comment:comment}
を記載します。
<%= render partial:'goods/good',locals:{comment:comment} %>
<% @comments.each do |comment| %>
<% if @question.id == comment.question_id %>
<div class="comment-box">
<% if comment.user.image? %>
<%= link_to (image_tag comment.user.image.thumb.to_s),user_path(comment.user.id) %>
<% else %>
<%= link_to (image_tag "default.png",class: "q-user-image"), user_path(comment.user.id), method: :get, alt:"ユーザーアイコン" %>
<% end %>
<%= comment.user.name %>
<%= comment.content %>
<%= time_ago_in_words(comment.created_at) %>前
<% if logged_in? %>
<%= render partial:'goods/good',locals:{comment:comment} %>
</div>
##③create,destroyのjs処理を追加
いいねボタンが押された時の処理を作成します。
idで指定されたいいねボタンが押されたら、部分テンプレート化されたいいね部分のみ更新する処理になっています。
ここでは、インスタンス変数を使います。
$('#good_<%= @comment.id %>').html("<%= j(render partial: 'goods/good', locals: {comment: @comment}) %>");
$('#good_<%= @comment.id %>').html("<%= j(render partial: 'goods/good', locals: {comment: @comment}) %>");
#終わりに
いいね機能の非同期処理を実装する中で、インスタンス変数とローカル変数の違い、resourceとresoursesの違いなど多くの学びがありました。
わかりにくい部分、間違えている部分などありましたらコメントいただけますと幸いです!