Rails:嵌套资源冲突,如何根据被调用的路由确定索引操作的范围

kno*_*opx 12 routes ruby-on-rails nested-resources

想象一下,你有两个定义的路线:

map.resources articles
map.resources categories, :has_many => :articles
Run Code Online (Sandbox Code Playgroud)

都可以通过助手/路径访问

articles_path # /articles
category_articles_path(1) # /category/1/articles
Run Code Online (Sandbox Code Playgroud)

如果您访问/articles,index行动从ArticlesController被执行.

如果你访问/category/1/articles,也会执行index动作ArticlesController.

那么,根据呼叫路由有条件地仅选择范围文章的最佳方法是什么?

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

Sim*_*tti 12

这里有两个选择,具体取决于您的逻辑和视图与范围的关联程度.让我进一步解释.

第一种选择是确定控制器内的范围,正如其他响应所解释的那样.我通常设置@scope变量以在模板中获得一些额外的好处.

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
Run Code Online (Sandbox Code Playgroud)

@scope变量的原因是您可能需要在单个操作之外知道请求的范围.假设您要显示视图中的记录数.您需要知道是否按类别过滤.在这种情况下,您只需要在每次检查时调用@scope.count@scope.my_named_scope.count不重复params[:category_id].

如果您的视图(具有类别的视图和没有类别的视图)非常相似,则此方法很有效.但是,按类别过滤的列表与没有类别的列表完全不同时会发生什么?这种情况经常发生:您的类别部分提供了一些以类别为中心的小部件,而您的文章部分提供了一些与文章相关的小部件和过滤器.此外,您的文章控制器有一些您可能想要使用的特殊before_filter,但是当文章列表属于某个类别时,您不必使用它们.

在这种情况下,您可能希望分离操作.

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
Run Code Online (Sandbox Code Playgroud)

现在按类别过滤的列表由其管理CategoriesController,它继承了所有控制器过滤器,布局,设置......而未过滤的列表由管理ArticlesController.

这通常是我最喜欢的选择,因为通过额外的操作,您不必通过大量的条件检查来混淆您的视图和控制器.