12

假设您有两条已定义的路线:

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

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

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

如果您访问/articles,则执行index操作 from ArticlesController

如果您访问/category/1/articles,也会执行index操作 from 。ArticlesController

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

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

4 回答 4

13

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

第一个选择是确定控制器内的范围,正如其他响应已经解释的那样。我通常设置一个@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

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

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

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

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

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

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

于 2009-11-28T08:56:35.273 回答
3

我经常喜欢把这些动作分开。当结果操作非常相似时,您可以通过查看 params[:category_id] 是否存在等来轻松分离控制器内部的范围(请参阅@SimoneCarletti 答案)。

通常,使用自定义路由在控制器中分离动作会给您最大的灵活性和清晰的结果。以下代码生成正常的路由助手名称,但路由被定向到控制器中的特定操作。

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

然后你可以在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
于 2011-09-29T15:59:00.817 回答
2

只有一个嵌套资源,使用基于参数的条件来确定它的范围将是最简单的方法。这可能是您的情况。

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

但是,根据模型的其他嵌套资源,坚持使用这种方法可能会变得非常乏味。在这种情况下,使用诸如resource_controllermake_resourceful 之类的插件将使这更简单。

class ArticlesController < ResourceController::Base
  belongs_to :category
end

这实际上会做你所期望的一切。它为您提供所有标准 RESTful 操作,并将自动设置/categories/1/articles.

于 2009-11-28T04:41:23.037 回答
1
if params[:category_id].blank?
  # all
else
  # find by category_id
end

我喜欢考虑独立于路线的动作。无论他们如何到达那里,都要做出合理的决定来做什么。

于 2009-11-28T02:13:55.207 回答