【问题标题】:Rails: Nested resources conflict, how to scope the index action depending on the called routeRails:嵌套资源冲突,如何根据调用的路由确定索引操作的范围
【发布时间】:2009-11-28 00:44:55
【问题描述】:

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

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

帮助程序/路径都可以访问

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

如果您访问/articles,则会执行来自ArticlesControllerindex 操作。

如果您访问/category/1/articles,来自ArticlesControllerindex 操作也会执行。

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

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

【问题讨论】:

    标签: ruby-on-rails routes nested-resources


    【解决方案1】:

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

    第一个选择是确定控制器内的范围,正如其他响应已经解释的那样。我通常会设置一个 @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.count@scope.my_named_scope.count,而不是每次都重复检查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 管理。

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

    【讨论】:

    • 这正是我面临的另一个问题,我也需要渲染不同的视图。
    • 另外,:shallow 在这条特定路线上有意义吗?
    • 如果你不使用 :shallow ,你需要一种方法来映射 /articles 路线,所以,是的,恕我直言,这是有道理的。
    • 您建议的路线输出来自rake routescategory_articles == ArticlesController#index 而不是CategoriesController#articles
    • 您的规则和输出仍然不匹配。也许你的意思是map.resources :categories, :member => { :articles => :get },它产生articles_category_path/category/1/articlesCategoriesController#articles
    【解决方案2】:

    我经常喜欢将这些动作分开。当结果操作非常相似时,您可以通过查看 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
    

    【讨论】:

      【解决方案3】:

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

      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 的范围。

      【讨论】:

      • 我已经在使用 resource_controller,但是如何在不覆盖 resource_controller“魔术”方法的情况下对结果集进行分页并呈现不同的视图?
      • 您可以覆盖集合设置方法并告诉它您希望如何对文章进行分页。在“Helpers”下的 resource_controller 自述文件中有示例代码。
      【解决方案4】:
      if params[:category_id].blank?
        # all
      else
        # find by category_id
      end
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-19
        • 2016-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-22
        • 2011-05-23
        相关资源
        最近更新 更多