Rails ビューからデータベース クエリを抽出する
11082 ワード
序章
Rails ビューの一部のロジックは避けられませんが、最近、特にデータベース クエリの場合、ビュー内の不要なロジックをモデルまたはコントローラーに移動しようとしています.この問題は、Rails Facebook クローンで Friendship の「Accept」ボタンをコーディングしているときに見えてきました.
Notifications#index ページから友達リクエストを承認または拒否できるように設定しました.問題は、Notifications#index 内からそれぞれのフレンドシップ オブジェクトを見つけなければならないことでした.これには、データベース クエリが必要でした.通知に関連付けられた Friendship オブジェクトを見つけるための正しいデータベース クエリをビューに書き込んだ後、ビューからそれを削除する方法を考え始めました.以下は、私が問題にどのようにアプローチしたかです.
すべての通知は
sender
と receiver
を参照し、どちらも User クラスに属しています.同様に、 Friendship オブジェクトは sender
と receiver
で開始されます.ユーザーが通知ページに多くの友達リクエストを持っている場合、受信者がそれぞれ「承認」または「拒否」をクリックしたときに、どの特定の友達リクエストが承認または拒否されているかを判断する必要があります.したがって、通知コレクションをレンダリングすると、
notification.sender
との友情がすぐに受け入れられるか、すぐに辞退されるかを見つけることができます.notification.receiver
. 「同意する」ボタンは次のとおりです.最初の「同意する」ボタン
<% friendship = Friendship.find_by(sender_id: notification.sender.id,
receiver_id: notification.receiver.id) %>
<%= button_to "Accept",
friendship_path(friendship),
method: :put,
params: { friendship: { status: 'accepted' } } %>
sender_id
にマッピングされた、現在のユーザーに送信されたすべてのフレンド リクエストのハッシュを作成することで、ローカル変数の必要性をなくすことができます.この状況では、現在のユーザーが常に受信者になり、現在のユーザーに送信されたフレンドシップ リクエストを見つけるには、 sender_id
のみが必要です.このメソッドを User のインスタンス メソッドにするのは理にかなっていると思いましたが、おそらくこれを行うより良い方法があります.コメントで教えてください!関連するモデルと関連付けは次のとおりです.関連モデル
ユーザー、フレンドシップ、通知
class User < ApplicationRecord
has_many :sent_notifications,
class_name: 'Notification',
foreign_key: 'sender_id',
dependent: :destroy
has_many :received_notifications,
class_name: 'Notification',
foreign_key: 'receiver_id',
dependent: :destroy
has_many :sent_pending_requests, -> { friendship_pending },
class_name: 'Friendship',
foreign_key: 'sender_id',
dependent: :destroy
has_many :received_pending_requests, -> { friendship_pending },
class_name: 'Friendship',
foreign_key: 'receiver_id',
dependent: :destroy
# ...
end
class Notification < ApplicationRecord
belongs_to :sender, class_name: 'User'
belongs_to :receiver, class_name: 'User'
#...
end
class Friendship < ApplicationRecord
enum status: %i[pending accepted declined]
belongs_to :sender, class_name: 'User'
belongs_to :receiver, class_name: 'User'
scope :friendship_pending, -> { where(status: :pending) }
#...
end
次に、送信者 ID を Friendship オブジェクトにマップするメソッド:
class User
#...associations, etc.
def requests_via_sender_id
requests = received_pending_requests
sender_ids = requests.pluck(:sender_id)
sender_ids.to_h do |id|
[id, requests.find_by(sender_id: id)]
end
end
end
現時点では
received_pending_requests.count == 1
としましょう. ID 3 のユーザーが current_user
リクエストを送信しました.current_user.requests_via_sender_id =>
{3=>
#<Friendship:0x000055f71022ec20
id: 16,
status: "pending",
sender_id: 3,
receiver_id: 1 }
このようにすることの良い点は、コントローラーが Notifications#index ビューをレンダリングする前に User モデルからこの情報を要求できるようになったことです.
class NotificationsController < ApplicationController
def index
@notifications = current_user.received_notifications.includes(%i[sender receiver])
@friendships = current_user.requests_via_sender_id # => returns a hash
end
end
最後に、レンダリング中の現在の通知にアクセスできる [Accept] ボタンに戻ります.
friendship
ローカル変数がないことに注意してください.最後の「同意する」ボタン
<%= button_to "Accept",
friendship_path(@friendships[notification.sender.id]),
method: :put,
params: { friendship: { status: 'accepted' } }%>
結論
このアプローチのもう 1 つの利点は、読みやすさの向上です.どの友情を更新していますか?通知の送信者が要求したもの -->
friendship_path(@friendships[notification.sender.id])
.これは、 Friendship オブジェクトを検索するコンテキストを提供するため、 friendship_path(friendship)
よりも表現力が高いことは間違いありません.モデル、ビュー、およびコントローラーの間で責任のバランスをとるのは難しい場合もありますが、少しずつソリューションに向かって進むことは、非常に教育的でやりがいのあることです.
これが私の最初のブログ投稿でした.読んでいただきありがとうございます.それを終えた後、私はそれが理解に関して持っている利点を理解しています.近いうちにもっと記事を書きたいと思っています.
Lawrence Hookhamの写真
Unsplash
Reference
この問題について(Rails ビューからデータベース クエリを抽出する), 我々は、より多くの情報をここで見つけました https://dev.to/joemccanndev/extracting-a-database-query-from-a-rails-view-ggeテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol