Rails Diary

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

いいね機能まとめ③(いいねした投稿一覧,N+1問題対策)

前回

okmt-aya-26.hatenablog.com

実装の流れ(続き)

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

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

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

  4. ルーティングの設定 

  5. いいねボタンビュー作成 

  6. favorites_controllerの実装 

  7. いいねした投稿一覧作成 ←ここから!

  8. 掲示板一覧の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起点で値を取得してほうがいいということ?🤔

続く>>

参考にしたサイト

N+1問題 - Qiita

【Rails】 N+1問題をincludesメソッドで解決しよう! | Pikawaka