7

在 config/routes.rb 中:

resources :posts do
  resources :comments
end

resources :pictures do
  resources :comments
end

我也想允许更多的东西被评论。

我目前正在使用mongoid(mongomapper 与 Rails 3 的兼容性还不如我想的那样),并且注释是嵌入式资源(mongoid 还不能处理多态关系资源),这意味着我确实需要父资源为了找到评论。

是否有任何优雅的方法来处理以下一些问题:

在我的控制器中,我需要先找到父级,然后才能找到评论:

if params[:post_id]
  parent = Post.find(params[:post_id]
else if params[:picture_id]
  parent = Picture.find(params[:picture_id]
end

如果我开始添加更多可评论的内容,这将变得一团糟。

url_for([comment.parent, comment])不起作用,所以我将不得不在我的Comment模型中定义一些东西,但我认为我还需要在Comment模型中定义一个索引路由以及可能的编辑和新路由定义。

随着我走得更远,我可能需要处理更多的问题。

我无法想象我是第一个尝试解决此问题的人,是否有任何解决方案可以使这更易于管理?

4

3 回答 3

4

我必须在我的应用程序中做类似的事情。我接受了我想出的东西并对其进行了一些更改,但我尚未对其进行测试,因此请小心使用。它不漂亮,但它比我能想到的其他任何东西都要好。

在 routes.rb 中:

resources :posts, :pictures

controller :comments do
  get '*path/edit' => :edit, :as => :edit_comment
  get '*path'      => :show, :as => :comment
  # etc. The order of these is important. If #show came first, it would direct /edit to #show and simply tack on '/edit' to the path param.
end

在comment.rb 中:

embedded_in :commentable, :inverse_of => :comments

def to_param
  [commentable.class.to_s.downcase.pluralize, commentable.id, 'comments', id].join '/'
end

在 comments_controller.rb 的前置过滤器中:

parent_type, parent_id, scrap, id = params[:path].split '/'

# Security: Make sure people can't just pass in whatever models they feel like
raise "Uh-oh!" unless %w(posts pictures).include? parent_type

@parent = parent_type.singularize.capitalize.constantize.find(parent_id)
@comment = @parent.comments.find(id)

好了,丑事结束。现在您可以向任何您想要的模型添加注释,并且只需执行以下操作:

edit_comment_path @comment
url_for @comment
redirect_to @comment

等等。

编辑:我没有在我自己的应用程序中实现任何其他路径,因为我需要的只是编辑和更新,但我想它们看起来像:

controller :comments do
  get    '*path/edit' => :edit, :as => :edit_comment
  get    '*path'      => :show, :as => :comment
  put    '*path'      => :update
  delete '*path'      => :destroy
end

其他动作会更棘手。您可能需要执行以下操作:

  get  ':parent_type/:parent_id/comments'     => :index, :as => :comments
  post ':parent_type/:parent_id/comments'     => :create
  get  ':parent_type/:parent_id/comments/new' => :new,   :as => :new_comment

然后,您将使用 params[:parent_type] 和 params[:parent_id] 访问控制器中的父模型。您还需要将正确的参数传递给 url 助手:

comments_path('pictures', 7)
于 2010-09-10T01:15:50.223 回答
2

Ryan Bates 在Railscasts #154中介绍了多态关联,但该示例适用于 Rails 2 和 Active Record。通过进行一些更改,我设法让他的示例使用 Rails 3 和 Mongoid 工作。

在 Post 和 Picture 模型中,添加以下行:

embeds_many :comments, :as => :commentable

根据Mongoid 关联文档,所有embedded_in关联都是多态的。使用 Mongoid 时不需要 Railscast 中提到的commentable_idandcommentable_type列,因为注释是可注释的子对象。在 Comment 模型中,添加以下行:

embedded_in :commentable, :inverse_of => :comment

在 config/routes.rb 中设置路由,如下所示:

resources posts do
  resources comments
end

resources pictures do
  resources comments
end

将以下方法作为private方法添加到您的评论控制器中。这与 Ryan 的方法相同:

def find_commentable  
  params.each do |name, value|  
    if name =~ /(.+)_id$/  
      return $1.classify.constantize.find(value)  
    end  
  end  
  nil  
end

在您需要查找评论的每个评论控制器操作中,首先调用 find_commentable 方法以获取父级。找到父级后,您可以通过搜索可评论的评论,按 ID 找到评论。例如,在编辑操作中,查找评论的代码如下所示:

@commentable = find_commentable  
@comment = @commentable.comments.find(params[:id])

为了减少在每个操作开始时调用 find_commentable 的重复,您可以在控制器顶部放置一个 before 过滤器,如下所示:

class CommentsController < ApplicationController
  before_filter :find_commentable
  ...

然后将 find_commentable 方法中的返回调用更改为:

return @commentable = $1.classify.constantize.find(value)

使用此方法我没有遇到任何问题,但是如果您遇到任何问题,请指出。

于 2010-12-23T04:45:54.470 回答
0

根据 Uriptical 的回答,我发现这些关系有效,但命名路线仍然没有。

我对 rails 3 还是很陌生,但我找到了一个使用 eval 的简单解决方案。

例如,在我的项目中,多态父项(在我的应用程序中表示为 mongoid 对象 Product 和 Category)使用 find_comentable 的修改定义为 @imagable,正在编辑的子项称为 @image。

诸如product_image_path(@imagable, @image)which does 之类的 urlGET => products/:product_id/images/可以替换为:

send("#{@imagable.class.name.downcase}_image_url", @imagable, image )

这适用于所有命名路径。例如:

link_to 'Edit', send("edit_#{@imagable.class.name.downcase}_image_path", @imagable, image ) 
link_to 'Destroy', send("#{@imagable.class.name.downcase}_image_url", @imagable, image), :confirm => 'Are you sure?', :method => :delete

这样做的缺点是它会在您的视图和控制器中发送所有您有重定向的地方。

有没有更优雅的解决方案可以通过路线做到这一点?

*用发送替换 eval

于 2011-01-29T23:43:43.477 回答