Rails:ネストされたリソースの競合、呼び出されたルートに応じてインデックスアクションをスコープする方法

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

質問

2つの定義されたルートがあるとします:

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

ヘルパー/パスでアクセス可能

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

/ articles にアクセスすると、 ArticlesController からの index アクションが実行されます。

/ category / 1 / articles にアクセスすると、 ArticlesController からの index アクションも実行されます。

では、呼び出しルートに応じてスコープ付き記事のみを条件付きで選択するための最良のアプローチは何ですか?

#if coming from the nested resource route
@articles = Articles.find_by_category_id(params[:category_id])
#else
@articles = Articles.all
役に立ちましたか?

解決

ここには、ロジックとビューがスコープに関連付けられている度合いに応じて、2つの選択肢があります。 さらに説明させてください。

最初の選択肢は、他の応答で既に説明したように、コントローラー内のスコープを決定することです。通常、テンプレートでいくつかの追加の利点を得るために@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変数の理由は、単一のアクション以外のリクエストのスコープを知る必要があるかもしれないからです。ビューにレコードの数を表示したいとします。カテゴリでフィルタリングしているかどうかを知る必要があります。この場合、 params [:category_id]のチェックを毎回繰り返すのではなく、単に @ scope.count または @ scope.my_named_scope.count を呼び出すだけです。

このアプローチは、カテゴリのあるビューとカテゴリのないビューが非常に似ている場合にうまく機能します。しかし、カテゴリーでフィルターされたリストがカテゴリーのないリストと比較して完全に異なる場合はどうなりますか?これは非常に頻繁に発生します。カテゴリセクションはカテゴリに焦点を合わせたウィジェットを提供し、記事セクションは記事に関連するウィジェットとフィルタを提供します。また、Articleコントローラーには使用したい特別な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

カテゴリでフィルタリングされたリストは CategoriesController で管理され、すべてのコントローラーフィルター、レイアウト、設定を継承します...フィルタリングされていないリストは ArticlesController 。

これは通常、追加のアクションを使用すると、大量の条件チェックでビューとコントローラーを乱雑にする必要がないため、私のお気に入りの選択です。

他のヒント

これらのアクションを分離したいことがよくあります。結果のアクションが非常に似ている場合、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

ネストされた単一のリソースのみを持ち、paramsに基づく条件を使用してスコープを決定するのが最も簡単なアプローチです。これはおそらくあなたの場合に行く方法です。

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 のスコープを自動的にセットアップします。

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

ルートから独立したアクションを検討したいと思います。どうやってそこにたどり着いたとしても、何をすべきかについて合理的な決定を下してください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top