울타리:중첩된 리소스 충돌, 호출된 경로에 따라 인덱스 작업 범위를 지정하는 방법

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

문제

두 개의 정의된 경로가 있다고 가정해 보세요.

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, index ~로부터의 행동 ArticlesController 실행도 됩니다.

그렇다면 호출 경로에 따라 범위가 지정된 기사만 조건부로 선택하는 가장 좋은 방법은 무엇입니까?

#if coming from the nested resource route
@articles = Articles.find_by_category_id(params[:category_id])
#else
@articles = Articles.all
도움이 되었습니까?

해결책

여기에서는 논리와 보기가 범위에 얼마나 묶여 있는지에 따라 두 가지 선택이 있습니다.더 자세히 설명하겠습니다.

첫 번째 선택은 다른 응답에서 이미 설명했듯이 컨트롤러 내의 범위를 결정하는 것입니다.나는 일반적으로 템플릿에서 몇 가지 추가적인 이점을 얻기 위해 @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_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 답변 참조).

일반적으로 사용자 정의 경로를 사용하여 컨트롤러의 작업을 분리하면 가장 유연하고 명확한 결과를 얻을 수 있습니다.다음 코드는 일반적인 경로 도우미 이름을 생성하지만 경로는 컨트롤러의 특정 작업으로 연결됩니다.

~ 안에 경로.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

그럼 당신은 할 수 있습니다 기사Controller.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

매개 변수를 기반으로 조건부를 사용하여 범위를 결정하는 것이 가장 쉬운 접근법 일 것입니다. 이것은 당신의 경우에가는 길 일 것입니다.

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

이것은 실제로 당신이 기대하는 모든 것을 할 것입니다. 그것은 당신에게 모든 표준 편안한 행동을 제공하고 스코프를 자동으로 설정합니다. /categories/1/articles.

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

나는 경로와 독립적 인 행동을 고려하고 싶습니다. 그들이 어떻게 거기에 도착하든, 무엇을 해야하는지에 대한 합리적인 결정을 내립니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top