Rotaie: conflitto di risorse nidificate, come ambito dell'azione dell'indice in base alla route chiamata

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

Domanda

Immagina di avere due percorsi definiti:

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

entrambi accessibili da helper / percorsi

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

se visiti / articoli , viene eseguita indice da ArticlesController .

se visiti / category / 1 / articoli , viene eseguita anche l'azione index da ArticlesController .

Quindi, qual è l'approccio migliore per selezionare in modo condizionale solo gli articoli con ambito in base al percorso di chiamata?

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

Soluzione

Hai due scelte qui, a seconda di quanto la tua logica e la tua vista siano legate all'ambito. Lasciatemi spiegare ulteriormente.

La prima scelta è determinare l'ambito all'interno del controller, come già spiegato dalle altre risposte. Di solito imposto una variabile @scope per ottenere alcuni vantaggi aggiuntivi nei miei modelli.

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

Il motivo della variabile @scope è che potrebbe essere necessario conoscere l'ambito della richiesta al di fuori della singola azione. Supponiamo che tu voglia visualizzare il numero di record nella tua vista. Devi sapere se stai filtrando per categoria o meno. In questo caso, devi semplicemente chiamare @ scope.count o @ scope.my_named_scope.count invece di ripetere ogni volta il controllo su params [: category_id] .

Questo approccio funziona bene se le tue viste, quella con categoria e quella senza categoria, sono abbastanza simili. Ma cosa succede quando l'elenco filtrato per categoria è completamente diverso rispetto a quello senza categoria? Ciò accade abbastanza spesso: la sezione della categoria fornisce alcuni widget incentrati sulla categoria mentre la sezione dell'articolo alcuni widget e filtri relativi all'articolo. Inoltre, il tuo controller dell'articolo ha alcuni filtri speciali prima che potresti voler usare, ma non devi usarli quando l'elenco degli articoli appartiene a una categoria.

In questo caso, potresti voler separare le azioni.

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

Ora l'elenco filtrato per categoria è gestito dal CategoriesController ed eredita tutti i filtri, i layout, le impostazioni del controller ... mentre l'elenco non filtrato è gestito dal ArticlesController .

Questa è di solito la mia scelta preferita perché con un'azione aggiuntiva non è necessario ingombrare le visualizzazioni e i controller con tonnellate di controlli condizionali.

Altri suggerimenti

Spesso mi piace separare quelle azioni. Quando le azioni risultanti sono molto simili è possibile separare facilmente gli ambiti all'interno del controller verificando la presenza di params [: category_id] & nbsp; ecc. (Vedere la risposta di @SimoneCarletti).

La separazione normale delle azioni nel controller tramite percorsi personalizzati offre la massima flessibilità e risultati chiari. Il seguente codice porta a nomi di helper di route normali ma i route sono diretti ad azioni specifiche nel controller.

In route.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

Quindi puoi avere in 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

Avere solo una singola risorsa nidificata, usare un condizionale basato sui parametri per determinare il suo ambito sarebbe l'approccio più semplice. Questo è probabilmente il modo di procedere nel tuo caso.

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

Tuttavia, a seconda di quali altre risorse nidificate hai per il modello, attenersi a questo approccio può diventare piuttosto noioso. In tal caso, utilizzando un plug-in come resource_controller o make_resourceful renderà tutto molto più semplice.

class ArticlesController < ResourceController::Base
  belongs_to :category
end

Questo effettivamente farà tutto ciò che ti aspetteresti. Ti dà tutte le tue azioni RESTful standard e imposterà automaticamente l'ambito per / categorie / 1 / articoli .

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

Mi piace considerare l'azione indipendente dalla rotta. Non importa come ci si arriva, prendere una decisione ragionevole su cosa fare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top