2009-11-28 8 views
12

Stellen Sie sich zwei definierten Routen haben:Rails: Verschachtelte Ressourcen Konflikt, wie man Rahmen der Aktion Index auf der genannten Strecke je

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

beide zugänglich Helfer/Pfade

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

, wenn Sie besuchen /articles, index Aktion von ArticlesController wird ausgeführt. Wenn Sie /category/1/articles, index aufrufen, wird die Aktion von ArticlesController ebenfalls ausgeführt.

Also, was ist der beste Ansatz für die bedingte Auswahl nur die Bereiche Artikel abhängig von der anrufenden Route?

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

Antwort

12

Sie haben hier zwei Möglichkeiten, abhängig davon, wie stark Ihre Logik und Ihre Ansicht an den Bereich gebunden ist. Lassen Sie mich weiter erklären.

Die erste Wahl besteht darin, den Umfang innerhalb Ihres Controllers zu bestimmen, wie bereits in den anderen Antworten erläutert. Normalerweise setze ich eine Variable @ Scope, um einige zusätzliche Vorteile in meinen Vorlagen zu erhalten.

Der Grund für die @ Scope-Variable ist, dass Sie möglicherweise den Umfang Ihrer Anfrage außerhalb der einzelnen Aktion kennen müssen. Angenommen, Sie möchten die Anzahl der Datensätze in Ihrer Ansicht anzeigen. Sie müssen wissen, ob Sie nach Kategorie filtern oder nicht. In diesem Fall müssen Sie einfach @scope.count oder @scope.my_named_scope.count aufrufen, anstatt jedes Mal die Überprüfung auf params[:category_id] zu wiederholen.

Dieser Ansatz funktioniert gut, wenn Ihre Ansichten, die mit Kategorie und die ohne Kategorie, sehr ähnlich sind. Aber was passiert, wenn der nach Kategorien gefilterte Eintrag völlig anders ist als der ohne Kategorie? Das passiert ziemlich oft: Ihr Kategorienbereich bietet einige kategorienorientierte Widgets, während Ihr Artikel einige Artikel-bezogene Widgets und Filter enthält. Außerdem verfügt Ihr Artikelcontroller über einige spezielle before_filters, die Sie möglicherweise verwenden möchten. Sie müssen diese Artikel jedoch nicht verwenden, wenn die Artikelliste zu einer Kategorie gehört.

In diesem Fall möchten Sie möglicherweise die Aktionen trennen.

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 

Nun ist die Liste nach Kategorien gefiltert wird durch die CategoriesController verwaltet und es erbt alle Controller Filter, Layouts, Einstellungen ... während die ungefilterte Auflistung der ArticlesController verwaltet wird.

Dies ist normalerweise meine Lieblingswahl, weil Sie mit einer zusätzlichen Aktion Ihre Ansichten und Controller nicht mit einer Unzahl von bedingten Prüfungen überladen müssen.

+0

Dies ist genau das andere Problem, mit dem ich konfrontiert war, ich musste auch verschiedene Ansichten wiedergeben. – knoopx

+0

auch, tut: flach macht Sinn auf dieser bestimmten Route? – knoopx

+0

Wenn du nicht: seicht verwendest, brauchst du eine Möglichkeit, die/articles-Route zu mappen, ja, IMHO macht es Sinn. –

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

Ich mag die Aktion unabhängig von der Route zu prüfen. Egal, wie sie dorthin kommen, treffen Sie eine vernünftige Entscheidung, was zu tun ist.

2

Wenn nur eine einzige verschachtelte Ressource vorhanden ist, wäre die Verwendung einer Bedingung basierend auf den Parametern zur Bestimmung des Bereichs der einfachste Ansatz. Dies ist wahrscheinlich der Weg in Ihrem Fall zu gehen.

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

jedoch je nachdem, welche anderen verschachtelten Ressourcen, die Sie haben für das Modell, mit diesem Ansatz stecken kann ziemlich mühsam bekommen. In diesem Fall wird die Verwendung eines Plugins wie resource_controller oder make_resourceful dies viel einfacher machen.

class ArticlesController < ResourceController::Base 
    belongs_to :category 
end 

Dies wird tatsächlich alles tun, was Sie erwarten würden. Es gibt Ihnen alle Ihre standardmäßigen RESTful-Aktionen und richtet automatisch den Bereich für /categories/1/articles ein.

+0

Ich bin schon resource_controller verwenden, aber, wie Sie die Suchresultates Paginieren würde und machen unterschiedliche Ansichten ohne überschreiben Resource_Controller "Magic" -Methode? – knoopx

+0

Sie können die Sammlungseinstellungsmethode überschreiben und angeben, wie die Artikel paginiert werden sollen. Es gibt Beispielcode in der Readme-Datei von resource_controller unter "Helpers". –

3

Ich mag es oft, diese Aktionen zu trennen. Wenn die resultierenden Aktionen sehr ähnlich sind, können Sie die Bereiche innerhalb des Controllers einfach trennen, indem Sie sehen, ob params [: category_id] vorhanden ist usw. (siehe @SimoneCarletti-Antwort).

Normalerweise trennen Sie die Aktionen im Controller, indem Sie benutzerdefinierte Routen verwenden, um maximale Flexibilität und klare Ergebnisse zu erzielen. Der folgende Code führt zu normalen Routenhelkennamen, die Routen werden jedoch an bestimmte Aktionen im Controller geleitet.

In 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 

Dann können Sie in ArticlesController.rb haben

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