代码之家  ›  专栏  ›  技术社区  ›  knoopx

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

  •  12
  • knoopx  · 技术社区  · 16 年前

    假设您有两个已定义的路径:

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

    都可由帮助者/路径访问

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

    如果你来访 /articles , index 行动从 ArticlesController 执行。

    如果你来 /category/1/articles , 指数 行动从 条款控制者 也被执行。

    那么,对于只根据调用路径有条件地选择作用域项目,最好的方法是什么?

    #if coming from the nested resource route
    @articles = Articles.find_by_category_id(params[:category_id])
    #else
    @articles = Articles.all
    
    4 回复  |  直到 15 年前
        1
  •  12
  •   Simone Carletti    16 年前

    根据逻辑和视图与范围的关联程度,这里有两个选择。 让我进一步解释。

    第一种选择是确定控制器内的作用域,正如其他响应所解释的那样。我通常设置一个@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-u过滤器,但是当文章列表属于一个类别时,您不必使用它们。

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

    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 .

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

        2
  •  3
  •   holli    15 年前

    我经常喜欢把这些动作分开。当结果操作非常相似时,您可以通过查看参数[:category_id是否存在等来轻松分离控制器内的作用域(请参见@simonecalletti answer)。

    通常,通过使用自定义路由分离控制器中的操作,可以提供最大的灵活性和清晰的结果。下面的代码将产生正常的路由助手名称,但这些路由将定向到控制器中的特定操作。

    在 惯例 :

    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
    

    那你就可以进去了 条款控制器.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
  •  2
  •   Michael Richards    16 年前

    只有一个嵌套的资源,使用基于参数的条件来确定它的作用域将是最简单的方法。在你的情况下,这很可能就是解决问题的方法。

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

    但是,根据模型的其他嵌套资源,坚持这种方法可能会变得相当乏味。在这种情况下,使用类似插件 resource_controller 或 make_resourceful 这会使事情简单得多。

    class ArticlesController < ResourceController::Base
      belongs_to :category
    end
    

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

        4
  •  1
  •   Jonathan Julian    16 年前
    if params[:category_id].blank?
      # all
    else
      # find by category_id
    end
    

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

    推荐文章