ここまでの前提/今回のゴール
さて、初学者向けのテキストを噛み砕いて解いてみるシリーズ、第3段となりました。
ここまでは、Twitterのようなアプリで、投稿に「いいね!」機能をつけることを目標に、以下のような手順でコードを書いてきました。
Railsで「いいね!」機能を作る - ①アソシエーションに別名をつける
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション
今回は、これらをさらに一歩進めて「いいね!」した投稿の「いいね!」を解除する機能を作ろうと思います。
ユーザーが投稿した内容について、ボタンを押すごとに「いいね!」と「いいね!解除」が切り替わり「いいね!」とそれをやめることができる仕様にしたいと思います。
View
今回はビューから実装をスタートします。
ビューはこんな感じで作成しました。
<!-- postsのインスタンスは、パーシャルに渡されている前提です。 -->
<% posts.each do |post| %>
<div>
<!-- 投稿者名・投稿日時・投稿内容 -->
<div>
<%= post.user.name %> posted at <%= post.created_at %>
</div>
<div>
<p><%= post.content %></p>
</div>
<!-- いいね!ボタン -->
<div>
<!-- すでに「いいね!」されている時の表示 -->
<% if current_user.favorites.include?(post) %>
<%= form_with(model: @like, url: like_path(id: post.id), local: true, method: :delete) do |f| %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.submit 'いいね!解除' %>
<% end %>
<% else %>
<!-- まだ「いいね!」していない時の表示 -->
<%= form_with(model: @like, url: likes_path, local: true) do |f| %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.submit 'いいね!' %>
<% end %>
<% end %>
</div>
</div>
<% end %>
一つ目のポイントですが、form_with(model: @like, url: ...)
の部分では、model: Like
ではなく、model: @like
を使わないとダメでした。
なぜかなと思いapiドキュメントを調べてみたものの、ここだけは解決せず。
ただ、こちらの記事によると「隠しフィールドでデータを送りたいときは@like」ということだそうなので、今回はここまでの理解でませることにします。
form_with(model: @like) do |f|
の形で、Railsは自動でリクエストの送信先URLを推測してくれますが、今回は、デフォルトのURLではエラーが出るので、url:
オプションでリクエストの送信先を指定します。
また、url: like_path(id: post.id)
でクリックした投稿のidがリクエストのparams
に含まれていることにも注目してください。
最後に、local: true
でajaxではない通信を指定し、method: :delete
でDELETEメソッドでRailsにリクエストを送信することを記載しています。
Controller
コントローラーの記載はこんな感じです。
class LikesController < ApplicationController
def create
current_user.like_this(clicked_post)
flash[:success] = '投稿に「いいね!」しました。'
redirect_back(fallback_location: root_path)
# 前回の記事からelse以下の記述は削除
end
# ↓前回の記事からここを追記!
def destroy
current_user.likes.find_by(post_id: params[:post_id]).destroy
flash[:danger] = '「いいね!」を解除しました。'
redirect_back(fallback_location: root_path)
end
private
def clicked_post
Post.find(params[:post_id])
end
end
まず、destroyアクションは、current_user.likes.find_by(post_id: params[:micropost_id]).destroy
で現在のユーザーの「いいね!」した投稿の中にクリックした投稿が含まれているか確認します。
クリックした投稿のidはparams[:post_id]
の形で取り出すことができます。(さっきurl: like_path(id: post.id)
の形で送りましたね!)
そして、投稿を見つけたらdestroy
をします。
上記で、destroy
アクションが実装できたので、create
アクションの記述も少し書き換えます。さらに不要になったコードは消しておきます。
以上で、いいね!/いいね解除機能の実装は完了です
@like
の構造だけ説明がつかなかったのが残念ですが、これで自信を持って説明できそうです
余裕があれば、これをさらに応用してユーザーのフォロー/フォロー解除機能も実装し、詳しく読み解いていきたいです。
ここまで読んでくだっさった皆様、ありがとうございました^^
追記
2020.09.29 一部メソッドをリファクタリングしました
その他
応用編で、こんなものも作りました。