いいね機能まとめ②(いいねボタンビュー、コントローラ)
前回
実装の流れ(続き)
中間テーブルfavoriteモデルの作成
アソシエーションを定義
一部ギミックをモデルに記載
ルーティングの設定
いいねボタンビュー作成 ←ここから!
favorites_controllerの実装 ←ここまで!
いいね一覧作成
N+1問題対策
5. いいねボタンビュー作成
いいねのビューは三つのパーシャルに分けて作る。
ある投稿をcurrent_userがいいねしたかどうかで分岐させる。
自分の失敗点としてはBookmarkビューファイルにパーシャルを作っていたので、色々上手く行かなかった。投稿の中にボタンが表示されるので、postビューのファイル内にいいねボタンのパーシャルを配置する。
- メインのいいねボタンパーシャル(_favorite_button.html.erb)
- いいねする前の☆マークのパーシャル(_unfavorite.html.erb)
- いいねした後の★マークのパーシャル(_favorite.html.erb)
いいねを外す(unfavorite)と☆が白くなり、いいねする(favorite)と★が黒くなるので上記のようなパーシャル設定にする。ここで何故かスッと理解できずに長いこと混乱した。。
既に作成している投稿一つのパーシャル(post.html.erb)にいいねボタンを挟む。user.rbに記載しているown?メソッドを利用し、current_userの投稿であれば、編集・削除ボタン(crud.html.erb)を表示し、そうでなければいいねボタン(_favorite.button.html.erb)を表示する分岐にする。
user.rb
def own?(object) id == object.user_id end
呼び出し元(ここではcurrent_user)のidが、引数として渡した投稿のuser_idと一致するかどうか判定するメソッド。呼び出し元のレシーバを表すselfは省略している。
前提
post/posts_controller
def index @posts = Posts.all.includes(:user).order(created_at: :desc) end
posts/index.html.erb
<%= render @boards %>
_post.html.erb
<% if current_user.own?(board) %> <%= render 'crud_menus', board: board %> <% else %> <%= render 'bookmark_button', board: board %> <% end %>
いいねボタン
user.rbに記載したfavorite?メソッドを利用し、いいね済みかどうかを判定する。
has_many :favorite_posts, through: :favorites, source: :post def favorite?(post) favorite_posts.include?(post) end
_favorite.button.html.erb
<% if current_user.favorite?(post) %> <%= render 'unfavorite', post: post %> <% else %> <%= render 'favorite', post: post %> <% end %>
↑current_userのfavorite_posts配列に指定したpostは含まれているか?で分岐する。含まれていればtrueなので、_unfavorite.html.erbをレンダリングする。
☆(favorite)と★(unfavorite)の分岐
ルーティングは下記を使用
resources :posts do resources :comments, only: %i[create], shallow: true get :favorites, on: :collection end resources :favorite, only: %i[create destroy]
↓
favorites_posts GET /posts/favorites(.:format) posts#favorites favorites POST /favorites(.:format) favorites#create favorite DELETE /favorites/:id(.:format) favorites#destroy
_favorite.html.erb
<%= link_to favorites_path(post_id: post.id), id: "js-favorite-button-for-post-#{post.id}",method: :post do%> <%= icon 'far', 'star' %> <% end %>
id: "js-favorite-button-for-post-#{post.id}"
はその投稿のIDが画面上で一意になるように指定font-awesome-sass
gemを使用しているので、view内で<%= icon 'far', 'star' %>
と書くことができる。
↑favorites_path(post_id: post.id)というパス指定について
今回のルーティングではfavoriteをpostにネストしていないので、favorites_pathというヘルパーメソッドによって生成されるURLは/favorites
であり、投稿のIDが指定されていない。これではどの投稿のいいねを作成するのか分からないため、(post_id: post.id)
と引数を渡してリクエストのパラメータを付与する必要がある。
詳しくは以下参考に
リクエストのパラメーター付与について - Rails勉強記録
_unfavorite.html.erb
<%= link_to favorite_path(current_user.favorites.find_by(post_id: post.id)), id: "js-favorite-button-for-post-#{post.id}", method: :delete do%> <%= icon 'fas', 'star' %> <% end %>
↑ favorite_path(current_user.favorites.find_by(post_id: post.id))というパス指定
destroyアクションへのルーティングを見てみると、/favorites/:id
とfavorite自身の:idが求められている。このため、current_user.favorites配列からpost_id: post.idであるfavoriteのデータを取得し、パスのヘルパーにそのパラメータを渡している。
6. favorites_controllerの実装
①favorites_controller作成
bundle exec rails g controller favorites
コントローラに実装するのはcreateとdestroyのみのため、ビューは必要ない。
② createアクション
user.rbに記載したギミックを使用
has_many :favorite_posts, through: :favorites, source: :post def favorite(post) favorite_posts << post end
def create post = Post.find(params[:post_id]) current_user.favorite(post) redirect_back fallback_location: root_path, success: t('defaults.message.favorite') end
createアクションの仕事
ある投稿を取得し、お気に入り配列の中にそれを追加する。追加できたら元いたページにリダイレクトし成功メッセージを出す。もし失敗したらfallback_locationで設定したroot_pathに飛ばす。
③ destroyメソッド
user.rbに記載したunfavoriteメソッドを使用
has_many :favorite_posts, through: :favorites, source: :post def unfavorite(post) favorite_posts.delete(post) end
def destroy post = current_user.favorites.find(params[:id]).post current_user.unfavorite(post) redirect_back fallback_location: root_path end
destroyアクションの仕事
まずどの投稿を削除するのか、postを取得する。postを取得する際のメソッドチェーンが複雑というか分かりにくくなりがち。
current_user.favorites配列の中からリクエストで送られてきたid(params[:id]で取得)を元に特定のfavoriteを取得する。そのfavoriteが所属しているpost(belogns_to :post)を取得する。
unfavoriteメソッドを用いて、配列からそのpostを削除する。
参考にしたサイト
SQLアンチパターン 複数の値を一つの列に格納するな : ITフロギストン~技術系ブログみたいなもの
掲示板にお気に入り機能を実装する① - Programming Learning Diary
https://railsguides.jp/active_record_validations.html#uniqueness