Rieles: conflicto de recursos anidados, cómo abarcar la acción del índice en función de la ruta llamada

StackOverflow https://stackoverflow.com/questions/1811163

Pregunta

Imagine que tiene dos rutas definidas:

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

ambos accesibles por ayudantes / caminos

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

si visita / articles , se ejecuta la acción index de ArticlesController .

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

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

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

Solución

Aquí tiene dos opciones, dependiendo de cuánto esté vinculada su lógica y su punto de 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. Por lo general, configuro 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

El motivo de la variable @scope es que es posible que necesite conocer el alcance de su solicitud fuera de la acción individual. Supongamos que desea mostrar el número de registros en su vista. Debe saber si está filtrando por categoría o no. En este caso, simplemente debe llamar a @ scope.count o @ scope.my_named_scope.count en lugar de repetir cada vez que verifique los parámetros de [: category_id] .

Este enfoque funciona bien si sus puntos de vista, el que tiene categoría y el 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 con bastante frecuencia: la sección de su categoría proporciona algunos widgets centrados en la categoría, mientras que la sección de su artículo contiene algunos widgets y filtros relacionados con el artículo. Además, su controlador de artículos tiene algunos antes_filtros especiales que puede 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 administrada por CategoriesController y hereda todos los filtros, diseños, configuraciones del controlador ... mientras que la lista sin filtrar es administrada por ArticlesController .

Esta suele ser mi opción favorita porque con una acción adicional no tiene que saturar sus vistas y controladores con toneladas de comprobaciones condicionales.

Otros consejos

A menudo me gusta separar esas acciones. Cuando las acciones resultantes son muy similares, puede separar fácilmente los ámbitos dentro del controlador al ver si los parámetros [: category_id] & nbsp; están presentes, etc. (consulte la respuesta de @SimoneCarletti).

Normalmente, la separación de acciones en el controlador mediante rutas personalizadas le brinda la mayor flexibilidad y resultados claros. El siguiente código da como resultado nombres de ayuda 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

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 anidados tenga para el modelo, seguir este enfoque puede ser bastante tedioso. En cuyo caso, usar un complemento como resource_controller o make_resourceful hará esto mucho más simple.

class ArticlesController < ResourceController::Base
  belongs_to :category
end

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

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

Me gusta considerar la acción independiente de la ruta. No importa cómo lleguen allí, tome una decisión razonable sobre qué hacer.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top