2011-03-25 9 views
5

tengo las siguientes rutas:rieles manera de manejar la acción que está disponible en múltiples rutas

resources :users do 
    # List reviews made by user 
    resources :reviews, :only => [ :index ] 
end 

resources :products do 
    # List reviews by product, and provide :product_id for creation 
    resources :reviews, :only => [ :index, :new, :create ] 
end 

# Other actions don't depend on other resources 
resources :reviews, :except => [ :index, :new, :create ] 

todo se ve bien, excepto ReviewsController#index:

def index 
    if params[:user_id] 
    @reviews = Review.find_all_by_user_id params[:user_id] 
    else 
    @reviews = Review.find_all_by_product_id params[:product_id] 
    end 
    respond_with @reviews 
end 

Me preguntaba si hay una solución estándar a la problema anterior, o si hay una mejor manera de hacerlo.

Respuesta

6

Lo que tiene allí está bien, pero si lo desea, puede usar dos acciones diferentes también. Este enfoque debería permitirle cambiar la vista más fácilmente más adelante, y es un poco más seguro.

match '/products/:product_id/reviews' => 'reviews#product_index' 
match '/users/:user_id/reviews' => 'reviews#user_index' 

También mantendrá su código de controlador de un poco más limpio y menos susceptible a las preguntas extrañas como /products/10/reviews?user_id=100 que daría lugar a comentarios del usuario que se muestra en lugar de una revisión del producto.

def product_index 
    @reviews = Review.find_all_by_product_id params[:product_id] 
    respond_with @reviews 
end 

def user_index 
    @reviews = Review.find_all_by_user_id params[:user_id] 
    respond_with @reviews 
end 

La otra alternativa es utilizar diferentes controladores, así:

match '/products/:product_id/reviews' => 'product_reviews#index' 
match '/users/:user_id/reviews' => 'user_reviews#index' 
+1

Muy buena sugerencia. Creo que debería deshacerme de mi mentalidad de "apegarse a las acciones RESTful" :) –

+1

A veces funciona, otras veces simplemente se pone en el camino. Si deseas mantenerte en acciones totalmente relajadas, puedes sobrescribir el controlador que también lo maneja, para que puedas tener un ProductReviewsController relajante y un UserController relajante. Por supuesto, ya está utilizando muchas declaraciones excepto y solo, por lo que el uso de coincidencias url explícitas, como mi ejemplo, podría ser la mejor solución para usted. –

+1

He escuchado que algunos se quedan demasiado envueltos en el descanso, y estoy de acuerdo en que a veces es necesario que estalle, pero parece que no es así. En realidad, al crear dos controladores, está entrando en el estilo de descanso, de dos recursos (solo la acción de índice). En este caso, lo mantendría en un controlador y cargaría apropiado para los params. – DGM

0
def index 
    key = [:user_id, :product_id].find{|k| params[k]} 
    @reviews = Review.where(key => params[key]).first 
    respond_with @reviews 
end 
+0

Por qué '.first' que usted va a dar sólo un comentario Este también no maneja las asociaciones polimórficas que las revisiones deben tener. – Galen

1

Algunos plugins tienen formas de cargar los recursos para usted, como declarative_authorization o cancán, estoy seguro de que hay otros.

Otras soluciones que he visto es hacer un método privado en el controlador para cargar el objeto, y en ese método es esencialmente la misma lógica que tiene aquí; simplemente lo mueve fuera de la acción del índice. El método también se puede llamar como un filtro anterior.

Otra forma de hacer que su lógica es comenzar con el objeto padre (bueno si usted necesita el objeto padre demasiado:?.

 
before_filter :load_collection, :only => :index 

private 
def load_collection 
    if params[:user_id] 
    @user = @parent = User.find(params[:user_id]) 
    else 
    @product = @parent = Product.find(params[:product_id]) 
    end 
    @reviews = @parent.reviews 
end 
Cuestiones relacionadas