Pergunta

Imagine que você tem duas rotas definidas:

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

ambos acessíveis por helpers / caminhos

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

Se você visitar /articles, ação index de ArticlesController é executado.

Se você visitar /category/1/articles, ação index de ArticlesController é executado também.

Então, qual é a melhor abordagem para a seleção condicionalmente apenas os artigos escopo dependendo da rota ligando?

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

Solução

Você tem duas opções aqui, dependendo de quanto a sua lógica e sua visão está ligada ao escopo. Deixe-me explicar melhor.

A primeira escolha é determinar o âmbito em seu controlador, como já explicado pelas outras respostas. Eu costumo definir uma variável @scope para obter alguns benefícios adicionais em meus modelos.

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

A razão para a variável @scope é que você pode precisar de saber o alcance do seu pedido fora da única ação. Vamos supor que você deseja exibir o número de registros na sua vista. Você precisa saber se você está filtrando por categoria ou não. Neste caso, você simplesmente precisa @scope.count chamada ou @scope.my_named_scope.count em vez de repetir cada vez que o controlo sobre params[:category_id].

Esta abordagem funciona bem se os seus pontos de vista, aquele com categoria e um sem categoria, são bastante semelhantes. Mas o que acontece quando a lista filtrada por categoria é completamente diferente em comparação com a outra sem uma categoria? Isto acontece muitas vezes: sua seção categoria fornece alguns widgets focado da categoria, enquanto a sua seção do artigo Alguns widgets artigo-relacionados e de filtro. Além disso, o artigo controlador tem alguns before_filters especiais que você pode querer usar, mas você não tem que usá-los quando o artigo listando pertence a uma categoria.

Neste caso, você pode querer separar as acções.

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

Agora, o anúncio filtrada por categoria é gerido pela CategoriesController e herda todos os filtros controlador, layouts, configurações ... enquanto a lista não filtrada é gerido pela ArticlesController.

Esta é normalmente a minha escolha favorita porque com uma ação adicional que você não tem que encher seus pontos de vista e controladores com toneladas de verificações condicionais.

Outras dicas

muitas vezes eu gosto de separar essas ações. Quando as ações resultantes são muito semelhantes você pode separar os escopos dentro do controlador fácil por ver se params. [: Category_id] está presente etc (ver resposta @SimoneCarletti)

Normalmente separando ações no controlador usando rotas personalizadas dá-lhe mais flexibilidade e resultados claros. Código a seguir resulta em nomes normais rota auxiliares, mas as rotas são direcionados para ações específicas no controlador.

Em 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

Em seguida, você pode ter em 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

Tendo apenas um único recurso aninhado, usando uma condicional com base nos parâmetros para determinar a sua extensão seria a abordagem mais fácil. Este é provavelmente o caminho a percorrer no seu caso.

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

No entanto, dependendo do que outros recursos aninhados que você tem para o modelo, furando com essa abordagem pode ficar muito tedioso. Nesse caso, usando um plugin como resource_controller ou make_resourceful vai fazer isso muito mais simples.

class ArticlesController < ResourceController::Base
  belongs_to :category
end

Isso vai realmente fazer tudo o que você esperaria. Dá-lhe todas as suas ações RESTful padrão e será automaticamente configurar o escopo para /categories/1/articles.

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

Eu gosto de considerar o independente ação da rota. Não importa como eles chegam lá, tomar uma decisão razoável sobre o que fazer.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top