2010-09-10 8 views
7

En config/routes.rb:¿Alguien tiene alguna sugerencia para administrar recursos anidados polimórficos en Rails 3?

resources :posts do 
    resources :comments 
end 

resources :pictures do 
    resources :comments 
end 

me gustaría permitir más cosas que hay que comentar también.

Actualmente estoy usando mongoid (mongomapper aún no es compatible con Rails 3 como me gustaría), y los comentarios son un recurso incrustado (mongoid aún no puede manejar recursos relacionales polimórficos), lo que significa que lo hago necesita el recurso padre para encontrar el comentario.

¿Hay alguna manera elegante de manejar algunos de los siguientes problemas:

En mi controlador, necesito encontrar a los padres antes de encontrar el comentario:

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

que va a causar problemas si Comienzo a agregar más cosas para ser commentable.

también url_for([comment.parent, comment]) no funciona, así que voy a tener que definir algo en mi modelo Comment, pero creo que también voy a tener que definir una ruta de índice en el modelo Comment así como potencialmente una editar y nueva definición de ruta.

Puede haber más problemas que tengo que tratar a medida que avanzo.

No me puedo imaginar que soy la primera persona que intenta resolver este problema, ¿hay alguna solución para que esto sea más manejable?

Respuesta

4

Tuve que hacer algo similar en una aplicación mía. Tomé lo que se me ocurrió y lo cambié un poco, pero no lo he probado, así que úselo con cuidado. No es bonito, pero es mejor que cualquier otra cosa en la que haya podido pensar.

En 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 

En comment.rb:

embedded_in :commentable, :inverse_of => :comments 

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

En una antes de filtro en 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, fealdad terminado. Ahora puede agregar comentarios a los modelos que desee y simplemente hacer:

edit_comment_path @comment 
url_for @comment 
redirect_to @comment 

Y así sucesivamente.

Edit: No llevó a cabo cualquier otra ruta en mi propia aplicación, porque todo lo que necesitaba era editar y actualizar, pero me imagino que se verían algo así como:

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

Las demás acciones será más complicado. Es probable que tenga que hacer algo como:

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 

A continuación, tendría acceso al modelo de matriz en el controlador usando params [: PARENT_TYPE] y params [: parent_id].También había necesidad de pasar los parámetros apropiados a los ayudantes url:

comments_path('pictures', 7) 
+0

En realidad, esto se ve muy bien; con algunos pequeños cambios, acabo de implementar esto, y en su mayor parte parece estar funcionando bien. ¿Dónde está la documentación para el truco de enrutamiento que usaste en la primera parte para que pueda averiguar cómo conectar las rutas de creación, actualización, eliminación e indexación también? – Ryan

+0

Está bien, creo que descubrí lo que estabas haciendo y por qué te parece feo. El comentario básicamente debe ser la última ruta definida porque coincidirá con cualquier cosa. Terminé usando parte de lo que estabas haciendo y parte de lo que estaba haciendo para implementar todo. Realmente aprecio tu ayuda. – Ryan

+0

Ah, bien, lo siento si no estaba claro. Esas rutas salpicadas pueden ser engañosas. – PreciousBodilyFluids

2

Ryan Bates cubierto asociaciones polimórficas en Railscasts #154, pero el ejemplo era para los carriles 2 y Active Record. Logré que su ejemplo funcionara usando Rails 3 y Mongoid haciendo algunos cambios.

En los modelos de postales y de imagen, añada la siguiente línea:

embeds_many :comments, :as => :commentable 

De acuerdo con la Mongoid associations documentation, todos embedded_in asociaciones son polimórficos. No necesita las columnas commentable_id y commentable_type mencionadas en Railscast cuando usa Mongoid, porque el comentario es un elemento secundario de lo comentable. En el modelo de comentario, añada la siguiente línea:

embedded_in :commentable, :inverse_of => :comment 

de configuración de las rutas en config/routes.rb como este:

resources posts do 
    resources comments 
end 

resources pictures do 
    resources comments 
end 

agregue el método siguiente a su controlador comentarios como método private. Esto es idéntico al método de Ryan:

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

En cada una de sus acciones del controlador comentarios en las que necesita para encontrar el comentario, llame al método find_commentable primero en obtener la matriz. Una vez que el padre ha sido encontrado, puede encontrar el comentario por ID, buscando en los comentarios del comentario. Por ejemplo, en la acción de editar el código para encontrar el comentario se vería así:

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

Para reducir la repetición de llamada find_commentable al comienzo de cada acción, se puede poner un antes filtro en la parte superior de la controlador de la siguiente manera:

class CommentsController < ApplicationController 
    before_filter :find_commentable 
    ... 

Y a continuación, cambiar la llamada de retorno en el método find_commentable a:

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

no he encontrado ningún problema usando esto se reunió hod, pero si encuentras algún problema, por favor indícalo.

0

Basándome en la respuesta de Uriptical, encontré que las relaciones funcionaban, pero las rutas con nombre seguían sin funcionar.

Todavía soy bastante nuevo en rails 3 pero encontré una solución simple usando eval.

Por ejemplo, en mi proyecto, los padres polimórficos (representados en mi aplicación como objetos monógamos Producto y Categoría) se definen como @imagable usando una modificación de find_comentable y el hijo que se está editando se conoce como @image.

un enlace como el que hace product_image_path(@imagable, @image) GET => products/:product_id/images/ se puede sustituir por:

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

Esto funciona para todas las rutas con nombre. Por ejemplo:

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 

La desventaja de esto es que deja envía por todas partes sus puntos de vista y en los controladores siempre que tengas redirecciones.

¿Existe una solución más elegante para hacer esto a través de las rutas?

* sustituido con eval enviar

+0

Puede usar [Rutas polimórficas] (http://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html), p. 'edit_polymorphic_path (imageable, image)' –

Cuestiones relacionadas