有没有人有任何管理Rails 3中的多态嵌套资源的技巧?

时间:2010-09-10 00:31:08

标签: ruby-on-rails-3 polymorphism mongoid nested-resources

在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模型以及可能的编辑和新路线定义。

随着我的进一步发展,我可能会遇到更多问题。

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

3 个答案:

答案 0 :(得分: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中的before过滤器中:

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)

答案 1 :(得分:2)

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

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

embeds_many :comments, :as => :commentable

根据Mongoid associations documentation,所有embedded_in个关联都是多态的。使用Mongoid时,您不需要Railscast中提到的commentable_idcommentable_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的重复次数,你可以将一个前置过滤器放在控制器的顶部,如下所示:

class CommentsController < ApplicationController
  before_filter :find_commentable
  ...

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

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

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

答案 2 :(得分:0)

借鉴Uriptical的答案,我发现这些关系有效,但命名路线仍然没有。

我在rails 3上仍然很新,但我找到了一个使用eval的简单解决方案。

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

product_image_path(@imagable, @image)之类的网址 GET => 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替换为发送

相关问题