前提・実現したいこと
LINEの様なメッセージ機能を実装しました。
モデルは下記にて記載致しますが、User
とMessage
モデルがEntry
とRoom
と言う中間テーブルを持っています。
ユーザーが他のユーザーとのトークルームを作成した時点で空のEntry
が作成されますが、Message
を持たないEntry
はトークルーム一覧画面(rooms/index.html.erb
)には表示しないようにしています。
メッセージのやり取りや表示関連は既に実装出来ているのですが、N+1問題に関して一つ問題点があります。(尚、N+1問題解決の為にBullet
と言うgem
を使用し、問題が感知された場合はポップアップが表示されるようになっています。)
Message
のないEntry
を持つユーザーがrooms/index.html.erb
を表示すると、以下のポップアップが表示されます。
Ruby
1AVOID eager loading detected 2 Entry => [:user] 3 Remove from your query: .includes([:user]) 4Call stack
こちらはrooms_controller.rb
において、N+1問題を避けるためにEntry.includes(:room, :user)
としている箇所があるのですが、この:user
部分を省けと言われているようです。
しかし、通常Message
を持ったEntry
で構成されたトークルーム一覧画面(rooms/index.html.erb
)を表示する際、N+1問題を避けるために.includes(:user)
は必要になります。
ですのでこれを省くと、Message
を持たないEntry
を抱えているユーザーのページ上では問題が解決しますが、Message
を持つEntry
のみを抱えているユーザーのページ上では、当然以下の様に逆に怒られます。
Ruby
1Use eager loading detected 2 Entry => [:user] 3 Add from your query: .includes([:user]) # :user 部分を付け加えて下さい。 4Call stack
該当のソースコード
user.rb
Ruby
1class User < ApplicationRecord 2 has_many :messages, dependent: :destroy 3 has_many :entries, dependent: :destroy 4 has_many :rooms, through: :entries 5end
entry.rb
class Entry < ApplicationRecord belongs_to :user belongs_to :room has_many :messages, through: :room end
room.rb
class Room < ApplicationRecord has_many :messages, dependent: :destroy has_many :entries, dependent: :destroy has_many :users, through: :entries end
message.rb
class Message < ApplicationRecord validates :content, {presence: true} belongs_to :user belongs_to :room has_one :entry, through: :room end
rooms_controller.rb
private def set_another_entries # ログイン中のユーザーの持つEntryを配列にする。 my_room_ids = @current_user.entries.pluck(:room_id) # 上で定義した「ログイン中のユーザーの持つEntry」の内、自分以外の相手一覧を表示する。 @another_entries = Entry.includes(:room, :user).where(room_id: my_room_ids).where('entries.user_id != ?', @current_user.id) @entries = @another_entries.includes(:messages).to_a # エントリーを配列にする。 .reject {|entry| entry.messages.blank?} # messageデータのないエントリーは表示において除外する。 .sort_by {|entry| entry.messages.order(created_at: :desc).first.created_at} # messageが作成された順で並べ替える。 .reverse # Entryを降順に表示する。 end
試したこと
そもそも、Message
があるかないかでなぜ:user
の部分が引っ掛かるのかがよく分かりませんでした。
「Nilでない場合のみ実行する」と言う意味合いで以下の様に&.
を用いて記述してみたりもしましたが、ダメでした…。
rooms_controller.rb
@another_entries = Entry&.includes(:room, :user).where(room_id: my_room_ids).where('entries.user_id != ?', @current_user.id)
どなたかご助言を頂けますと有難いです。
補足情報(FW/ツールのバージョンなど)
ruby 2.6.4p104
RubyGems 3.0.3
Rails 5.2.3
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/03/01 05:42
2021/03/01 06:17
2021/03/01 06:41
2021/03/01 06:57