Rails Diary

プログラミングの学習記録です。

いいね機能まとめ②(いいねボタンビュー、コントローラ)

前回

okmt-aya-26.hatenablog.com

実装の流れ(続き)

  1. 中間テーブルfavoriteモデルの作成

  2. アソシエーションを定義

  3. 一部ギミックをモデルに記載

  4. ルーティングの設定 

  5. いいねボタンビュー作成 ←ここから!

  6. favorites_controllerの実装 ←ここまで!

  7. いいね一覧作成

  8. 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-sassgemを使用しているので、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