我目前正在為我的一個模型添加評論,MaintenanceRequest并想知道我應該如何處理。
我有一個Offer已經建立了評論的模型,它是用 ActionCable 設定的,如下所示
# models/comment.rb
class Comment < ApplicationRecord
belongs_to :user
belongs_to :offer
end
# channels/comment_channel.rb
class CommentChannel < ApplicationCable::Channel
def subscribed
offer = Offer.find params[:offer]
stream_for offer
end
end
并且評論控制器看起來像
# controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
before_action :is_valid_offer
def create
offer = Offer.find(comment_params[:offer_id])
if comment_params[:content].blank?
# return redirect_to request.referrer, alert: 'Comment cannot be empty.'
return render json: {success: false}
end
if offer.user_id != current_user.id && offer.landlord_id != current_user.id
return render json: {success: false}
end
@comment = Comment.new(
user_id: current_user.id,
offer_id: offer.id,
content: comment_params[:content],
)
if @comment.save
# redirect_to request.referrer, notice: 'Comment sent.'
CommentChannel.broadcast_to offer, message: render_comment(@comment)
render json: {success: true}
else
redirect_to request.referrer, alert: "Couldn't send comment."
render json: {success: true}
end
end
end
我的問題是,有沒有一種方法可以將相同的模型用于屬于評論的模型,MaintenanceRequest還是應該為評論創建新模型和控制器?如果我嘗試重用當前的評論模型,這似乎會變得非常混亂。這里的“最佳實踐”是什么?
uj5u.com熱心網友回復:
如果要創建可重用關聯,則要使用多型關聯:
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable,
polymorphic: true
validates :content,
presence: true
end
不是使用包含指向固定表的整數的單個列,而是通過使用一列作為其他表的主鍵并將物體的類名存盤在字串列中(在本例中稱為commentable_type)。
您可以通過在生成遷移(或模型)時傳遞多型選項來創建所需的列:
rails g migration add_commentable_to_comments commentable:belongs_to{polymorphic}
然而,控制器的動作有點像火車失事。它應該看起來更像這樣:
class CommentsController < ApplicationController
before_action :authenticate_user!
before_action :set_offer
before_action :authorize_user
def create
@comment = @offer.comments.new(comment_params) do |c|
c.user = current_user
end
if @comment.save
# redirect_to request.referrer, notice: 'Comment sent.'
CommentChannel.broadcast_to offer, message: render_comment(@comment)
# rendering json here is actually just stupid. Just use a HTTP status code
# as a response if you're not actually returning the rendered entity.
render json: { success: true },
status: :created,
location: @comment
else
# either return JSON which is actually useful or just a status code
render json: { success: false },
status: :unprocessable_entity
end
end
private
def set_offer
@offer = Offer.find(params[:offer_id])
end
# this should be extracted to an authorization layer such as Pundit
def authorize_offer
if @offer.user_id != current_user.id && @offer.landlord_id != current_user.id
render json: { success: false },
status: :unauthorized
end
end
end
有兩種方法可以將此控制器重用于不同型別的“評論”。您可以檢查單個控制器中是否存在offer_id或maintenance_request_id并使用它來推斷類,或者您可以使用繼承來更好地分配職責:
# routes.rb
resources :offers do
resources :comments,
only: :create,
module: :offers
end
resources :maintainence_requests do
resources :comments,
only: :create,
module: :maintainence_requests
end
class CommentsController
before_action :set_commentable, only: [:new, :create, :index]
before_action :authenticate_user!
attr_reader :commentable
def create
@comment = commentable.comments.new(comment_params) do |c|
c.user = current_user
end
if @comment.save
# simple extendability feature that lets you "tap into" the flow
# by passing a block when calling super in subclasses
yield @comment if block_given?
render json: @comment,
status: :created
else
render json: { errors: @comment.errors.full_messages },
status: :unprocessable_entity
end
end
private
# Uses simple heuristics based on module nesting to guess the name of the model
# that is being commented upon. Overide if needed.
# @example Foos::BarsController -> Foo
def commentable_class
@commentable_class ||= self.class.module_parent.name.singularize.constantize
end
def commentable_param_key
commentable_class.model_name.param_key
end
def set_commentable
@commentable = commentable_class.find("#{commentable_param_key}_id")
end
def comment_params
params.require(:comment)
.permit(:content)
end
end
module Offers
class CommentsController < ::CommentsController
# POST /offers/:offer_id/comments
def create
# block is called after a comment is succesfully saved but before
# the response is set
super do |comment|
CommentChannel.broadcast_to comment.offer,
message: render_comment(comment)
end
end
end
end
module MaintainenceRequests
class CommentsController < ::CommentsController
end
end
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/348317.html
上一篇:Ruby:如何知道是否有任何祖先覆寫了#initialize以有條件地呼叫`super`
下一篇:RubyTK如何接收用戶的輸入?
