2009-11-28 9 views
12

Imagine que tiene dos rutas definidas:Rieles: anidados conflicto de recursos, cómo se alcance la acción index dependiendo de la ruta llamada

map.resources articles 
map.resources categories, :has_many => :articles 

tanto accesible por ayudantes/caminos

articles_path # /articles 
category_articles_path(1) # /category/1/articles 

si visitas /articles, index se ejecuta la acción desde ArticlesController.

si visita /category/1/articles, index acción de ArticlesController se ejecuta también.

Entonces, ¿cuál es el mejor enfoque para seleccionar condicionalmente solo los artículos de ámbito dependiendo de la ruta de la llamada?

#if coming from the nested resource route 
@articles = Articles.find_by_category_id(params[:category_id]) 
#else 
@articles = Articles.all 

Respuesta

12

Aquí tiene dos opciones, dependiendo de cuánto se vincule su lógica y su vista al alcance. Déjame explicarte más.

La primera opción es determinar el alcance dentro de su controlador, como ya se explicó en las otras respuestas. Normalmente establezco una variable @scope para obtener algunos beneficios adicionales en mis plantillas.

class Articles 

    before_filter :determine_scope 

    def index 
    @articles = @scope.all 
    # ... 
    end 

    protected 

    def determine_scope 
     @scope = if params[:category_id] 
     Category.find(params[:category_id]).articles 
     else 
     Article 
     end 
    end 

end 

La razón de la variable @scope es que puede que tenga que conocer el alcance de su solicitud fuera de la única acción. Supongamos que desea mostrar el número de registros en su vista. Necesita saber si está filtrando por categoría o no. En este caso, solo necesita llamar al @scope.count o @scope.my_named_scope.count en lugar de repetir cada vez que se verifique params[:category_id].

Este enfoque funciona bien si sus vistas, la que tiene categoría y la que no tiene categoría, son bastante similares. Pero, ¿qué sucede cuando la lista filtrada por categoría es completamente diferente en comparación con la que no tiene una categoría? Esto sucede bastante a menudo: la sección de su categoría proporciona algunos widgets centrados en la categoría, mientras que la sección del artículo incluye algunos widgets y filtros relacionados con el artículo. Además, su controlador de artículo tiene algunos before -filters especiales que quizás quiera usar, pero no tiene que usarlos cuando la lista de artículos pertenece a una categoría.

En este caso, es posible que desee separar las acciones.

map.resources articles 
map.resources categories, :collection => { :articles => :get } 

articles_path # /articles and ArticlesController#index 
category_articles_path(1) # /category/1/articles and CategoriesController#articles 

Ahora la lista filtrada por categoría es administrado por el CategoriesController y hereda todos los filtros, los diseños, los ajustes del regulador ... mientras que la lista sin filtrar es dirigido por el ArticlesController.

Normalmente esta es mi opción favorita porque con una acción adicional no tiene que saturar sus vistas y controladores con toneladas de comprobaciones condicionales.

+0

este es exactamente el otro problema al que me enfrentaba, necesitaba renderizar diferentes vistas también. – knoopx

+0

también, does: shallow tiene sentido en esta ruta en particular? – knoopx

+0

Si no utiliza: superficial necesita una forma de asignar la ruta/articles así que, sí, en mi humilde opinión, tiene sentido. –

1
if params[:category_id].blank? 
    # all 
else 
    # find by category_id 
end 

me gustaría tener en cuenta la acción independiente de la ruta. No importa cómo lleguen allí, tome una decisión razonable sobre qué hacer.

2

Tener un solo recurso anidado, usar un condicional basado en los parámetros para determinar su alcance sería el enfoque más fácil. Este es probablemente el camino a seguir en su caso.

if params[:category_id] 
    @articles = Category.find(params[:category_id]).articles 
else 
    @articles = Article.all 
end 

Sin embargo, dependiendo de qué otros recursos anidada que tiene para el modelo, el de mantener este enfoque puede ser bastante tedioso. En ese caso, usar un complemento como resource_controller o make_resourceful hará que esto sea mucho más simple.

class ArticlesController < ResourceController::Base 
    belongs_to :category 
end 

Esto realmente hará todo lo que cabría esperar. Le proporciona todas sus acciones RESTful estándar y configurará automáticamente el alcance para /categories/1/articles.

+0

ya estoy usando resource_controller pero ¿cómo paginar el conjunto de resultados y hacer diferentes vistas sin anular el método "magic" de resource_controller? – knoopx

+0

Puede anular el método de establecimiento de colecciones y decirle cómo desea paginar los artículos. Hay un código de ejemplo en el readme del resource_controller en "Helpers". –

3

A menudo me gusta separar esas acciones. Cuando las acciones resultantes son muy similares, puede separar los ámbitos dentro del controlador fácilmente al ver si params [: category_id] está presente, etc. (vea la respuesta de @SimoneCarletti).

Normalmente, separar las acciones en el controlador mediante el uso de rutas personalizadas le ofrece la mayor flexibilidad y resultados claros. El siguiente código da como resultado nombres de ayudantes de ruta normales, pero las rutas se dirigen a acciones específicas en el controlador.

En routes.rb:

resources categories do 
    resources articles, :except => [:index] do 
    get :index, :on => :collection, :action => 'index_articles' 
    end 
end 
resources articles, :except => [:index] do 
    get :index, :on => :collection, :action => 'index_all' 
end 

, entonces puede tener en ArticlesController.rb

def index_all 
    @articles = @articles = Articles.all 
    render :index # or something else 
end 

def index_categories 
    @articles = Articles.find_by_category_id(params[:category_id]) 
    render :index # or something else 
end 
Cuestiones relacionadas