LoginSignup
1
1

Railsでいいね機能:ビューファイル中で使用しているいいね判定メソッドでn+1問題が発生した時の対処法

Last updated at Posted at 2024-06-20

読んで欲しい人

  • Railsでいいね機能を実装していて
    • ビューのループ中でクエリを発行するメソッドを使用している人
    • SQLクエリがたくさん出てきて、n+1問題が発生している人
  • 過去の自分

動作環境

  • MacOS 14.5
  • ruby 3.3.0
  • Rails 7.1.3.3
  • psql (PostgreSQL) 14.11

作っているアプリの構造

概要

  • 小さいX(旧ツイッター)みたいなアプリ
    • ログイン機能
    • 短文の投稿機能
    • いいね機能

テーブル設計

image.png

エラー・課題

app/models/user.rb:23:in `already_liked?'
16:48:02 web.1  |   Rendered posts/_like_btn.html.haml (Duration: 1.6ms | Allocations: 609)
16:48:02 web.1  |   Rendered posts/_follow_btn.html.haml (Duration: 0.0ms | Allocations: 12)
16:48:02 web.1  |   Like Exists? (0.3ms)  SELECT 1 AS one FROM "likes" WHERE "likes"."user_id" = $1 AND "likes"."post_id" = $2 LIMIT $3  [["user_id", 2], ["post_id", 5], ["LIMIT", 1]]


これがめっちゃ出てくる。
クエリがたくさん発行されてn+1問題になってしまっている

原因

  • いいねボタンを表示する時にalready_liked?が何回も実行されて、投稿の回数分のクエリが発行されてしまっている

いいねボタンを表示する仕組み

_like_btn.html.haml
# いいねボタンを表示するファイル
-if current_user.already_liked?(post)
  = button_to post_likes_path(post_id: post), method: :delete do
    %i.fa-regular.fa-hear # → いいねしてない時のアイコン
- else
  = button_to post_likes_path(post_id: post), method: :post do
    %i.fa-solid.fa-heart # → いいねしてる時のアイコン

already_liked?メソッド

  • 自分がその投稿をいいねしているのか、していないのかを判定するメソッド
user.rb
# モデルファイル
def already_liked?(post)
  likes.exists?(post_id: post.id)
end

exists?メソッドとは

  • 指定した条件のレコードがデータベースに存在するかどうかをture/falseで返すメソッド

  • SQLを発行するメソッド

# 使い方
Like.exists?(post_id: 3)

# 発行されるSQL
#SELECT 1 AS one FROM "likes" WHERE "likes"."post_id" = $3 LIMIT $1

解決方法

  • 事前に配列でlikesテーブルからpost_idの配列を取得して、include?メソッドでいいね判定を行うようにする
likes_controller.rb
#コントローラ
def index
  @liked_post_ids = current_user ? current_user.likes.pluck(:post_id) : []
end
view
#ビュー画面で使うとき
-if liked_post_ids.include?(post.id)
  = button_to post_likes_path(post_id: post), method: :delete do
    %i.fa-regular.fa-hear # → いいねしてない時のアイコン
- else
  = button_to post_likes_path(post_id: post), method: :post do
    %i.fa-solid.fa-heart # → いいねしてる時のアイコン

pluckメソッドとは

  • テーブルから指定したカラムを取得するクエリを送信できる
# 使い方
Like.pluck(:post_id)

# 発行されるSQL
#Like Pluck (0.9ms)  SELECT "likes"."post_id" FROM "likes"

# 戻り値:post_idを配列にしたもの
[1, 2, 3, 5]

※current_userがない時にから配列を返している理由

  • nilに対して.include?をするとエラーになってしまうため

学び

  • コードを記述するときは、そのメソッドがどんなクエリを発行しているのか?を気にして書くこと

  • DBとのやり取りを意識してコードを書くこと

参考記事

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1