有人对在 Rails 3 中管理多态嵌套资源有什么建议吗?

发布于 2024-09-18 16:15:43 字数 821 浏览 6 评论 0原文

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

随着我的进一步发展,可能会有更多的问题需要处理。

我无法想象我是第一个尝试解决这个问题的人,有什么解决方案可以让这个问题更容易管理吗?

In config/routes.rb:

resources :posts do
  resources :comments
end

resources :pictures do
  resources :comments
end

I would like to allow for more things to be commented on as well.

I'm currently using mongoid (mongomapper isn't as compatible with Rails 3 yet as I would like), and comments are an embedded resource (mongoid can't yet handle polymorphic relational resources), which means that I do need the parent resource in order to find the comment.

Are there any elegant ways to handle some of the following problems:

In my controller, I need to find the parent before finding the comment:

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

which is going to get messy if I start adding more things to be commentable.

Also url_for([comment.parent, comment]) doesn't work, so I'm going to have to define something in my Comment model, but I think I'm also going to need to define an index route in the Comment model as well as potentially an edit and new route definition.

There might be more issues that I have to deal with as I get further.

I can't imagine I'm the first person to try and solve this problem, are there any solutions out there to make this more manageable?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风蛊 2024-09-25 16:15:43

我必须在我的应用程序中做类似的事情。我把我想出的东西做了一些改变,但我还没有测试过,所以请小心使用。它不漂亮,但比我能想到的任何东西都要好。

在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)

I had to do something similar in an app of mine. I took what I came up with and changed it around a bit, but I haven't tested it, so use with care. It's not pretty, but it's better than anything else I was able to think of.

In 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

In comment.rb:

embedded_in :commentable, :inverse_of => :comments

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

In a before filter in 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)

Ok, ugliness over. Now you can add comments to whatever models you want, and simply do:

edit_comment_path @comment
url_for @comment
redirect_to @comment

And so on.

Edit: I didn't implement any other paths in my own app, because all I needed was edit and update, but I'd imagine they'd look something like:

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

The other actions will be trickier. You'll probably need to do something like:

  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

You'd then access the parent model in the controller using params[:parent_type] and params[:parent_id]. You'd also need to pass the proper parameters to the url helpers:

comments_path('pictures', 7)
鸠书 2024-09-25 16:15:43

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

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

embeds_many :comments, :as => :commentable

根据 Mongoid 关联文档,所有 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 的重复次数,您可以在控制器顶部放置一个 before 过滤器,如下所示:

class CommentsController < ApplicationController
  before_filter :find_commentable
  ...

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

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

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

Ryan Bates covered polymorphic associations in Railscasts #154, but the example was for Rails 2 and Active Record. I managed to get his example working using Rails 3 and Mongoid by making a few changes.

In the Post and Picture models, add the following line:

embeds_many :comments, :as => :commentable

According to the Mongoid associations documentation, all embedded_in associations are polymorphic. You don't need the commentable_id and commentable_type columns mentioned in the Railscast when using Mongoid, because the comment is a child of the commentable. In the Comment model, add the following line:

embedded_in :commentable, :inverse_of => :comment

Setup the routes in config/routes.rb like this:

resources posts do
  resources comments
end

resources pictures do
  resources comments
end

Add the following method to your comments controller as a private method. This is identical to Ryan's method:

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

In each of your comments controller actions where you need to find the comment, call the find_commentable method first to get the parent. Once the parent has been found, you can find the comment by ID, by searching through the commentable's comments. For example, in the edit action the code to find the comment would look like this:

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

To reduce the repetition of calling find_commentable at the start of every action, you could put a before filter at the top of the controller like this:

class CommentsController < ApplicationController
  before_filter :find_commentable
  ...

And then change the return call in the find_commentable method to:

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

I haven't encountered any problems using this method, but if you come across any issues please point them out.

金橙橙 2024-09-25 16:15:43

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

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

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

诸如 product_image_path(@imagable, @image) 之类的 url
<代码>获取=> 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

这样做的缺点是它会将发送发送到您的视图和控制器中,无论您在哪里进行重定向。

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

*用 send 替换 eval

Drawing on Uriptical's answer, I found the relationships to work but named routes still did not.

I'm still pretty new at rails 3 but I found a simple solution using eval.

For instance, in my project, the polymorphic parents (represented in my app as the mongoid objects Product and Category) are defined as @imagable using a modification of find_comentable and the child being edited is referred to as @image.

a url such as product_image_path(@imagable, @image) which does
GET => products/:product_id/images/ can be replaced with:

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

This works for all named paths. For instance:

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

The downside to this is it leaves sends all over your views and in controllers wherever you have redirects.

Is there a more elegant solution to do this via routes?

*replaced eval with send

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文