いいね機能まとめ③(いいねした投稿一覧,N+1問題対策)
前回
実装の流れ(続き)
中間テーブルfavoriteモデルの作成
アソシエーションを定義
一部ギミックをモデルに記載
ルーティングの設定
いいねボタンビュー作成
favorites_controllerの実装
いいねした投稿一覧作成 ←ここから!
掲示板一覧のN+1問題対策 ←ここまで!
7. いいねした投稿一覧の表示
① いいねした投稿を全て取得するアクションを作成。
ルーティングは下記の記述
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
URLの表記を直感的に分かりやすくするため、collectionを用いてfavoritesアクションへのルートを作成している。postsコントローラにはfavoritesアクションを作成する。
postsコントローラ
def favorites @favorite_posts = current_user.favorite_posts.includes(:user).order(created_at: :desc) end
②ビューの作成
postsディレクトリにfavorites.htm.erbファイルを作成する。
posts#favoritesで取得した@favorite_postsをレンダリングする。
<%= content_for :title, t('.title') %> <div class="container pt-3"> <div class="row"> <div class="col-12"> <div class="row"> <% if @favorite_posts.present? %> <%= render @favorite_posts %> <% else %> <p><%= t('.no_result')%></p> <% end %> </div> </div> </div> </div>
8. 掲示板一覧のN+1問題対策
ぼんやりとしか分からないので追加で調べる
現状
def index @posts = Post.all.includes(:user).order(created_at: :desc) end
userを取得する問題は解決しているが、favoriteもキャッシュしたい。
変更後
def index @posts = Post.all.includes([:user, :favorites]).order(created_at: :desc) end
user.rbに記載しているロジックも・・
現状
def favorite?(post) favorite_posts.include?(post) end
- userを起点にbookmark_boardsを取りに行くので、これだと毎回SQLが走ってしまう。
- コントローラ側でキャッシュしているのはあくまでもpost_idを元に検索したfavorites
変更後
def favorite?(post) post.favorites.pluck(:user_id).include?(:id) end
↑の場合、postを起点に検索をかけに行くのでN+1問題が起きない。
_unfavorite.html.erb
現状
<%= link_to favorite_path(current_user.favorites.find_by(post_id: post.id)), id: "js-favorite-button-for-post-#{post.id}", class:"float-right", method: :delete, remote: true do %> <%= icon 'fas', 'star' %> <% end %>
変更後
<%= link_to favorite_path(post.favorites.find { |p| p.user_id == current_user.id }), id: "js-favorite-button-for-post-#{post.id}", class:"float-right", method: :delete, remote: true do %> <%= icon 'fas', 'star' %> <% end %>
- current_user.favorites.find_by(post_id: post.id)のようにするとN+1問題が発生してしまうので単なるRubyメソッドであるfindを使った方が良いらしい。
userを起点に値を取得するとN+1問題が起こってしまうので、N+1問題の起こらないpost起点で値を取得してほうがいいということ?🤔
続く>>