Rails: Verschachtelte Ressourcen Konflikt, wie Umfang der Index Aktion in Abhängigkeit von der genannten Route
-
06-07-2019 - |
Frage
Stellen Sie sich vor, Sie haben zwei definierten Routen:
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 /articles
, index
Aktion von ArticlesController
besuchen ausgeführt wird.
Wenn Sie /category/1/articles
besuchen, index
Aktion von ArticlesController
wird auch ausgeführt.
Was ist der beste Ansatz für bedingt nur den scoped Artikel Auswahl 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
Lösung
Sie haben zwei Möglichkeiten hier, je nachdem, wie viel Sie Ihre Logik und Ihre Sicht auf den Umfang gebunden ist. Lassen Sie mich erklären weiter.
Die erste Wahl ist, den Umfang innerhalb des Controllers, um zu bestimmen, wie bereits von den anderen Antworten erklärt. Ich in der Regel eine @scope Variable einige zusätzliche Vorteile in meiner Vorlagen zu erhalten.
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
Der Grund für die @scope Variable ist, dass Sie den Umfang Ihrer Anfrage außerhalb der einzelnen Maßnahmen wissen brauchen könnten. Nehmen wir an, Sie die Anzahl der Datensätze in der Ansicht angezeigt werden sollen. Sie müssen wissen, ob Sie nach Kategorie sind Filterung oder nicht. In diesem Fall müssen Sie einfach @scope.count
oder @scope.my_named_scope.count
rufen, anstatt jedes Mal die Prüfung auf params[:category_id]
zu wiederholen.
Dieser Ansatz funktioniert gut, wenn Sie Ihre Ansichten, die mit Kategorie und das eine ohne Kategorie, sind recht ähnlich. Aber was passiert, wenn der durch Kategorie gefiltert Eintrag ist völlig anders im Vergleich zu dem ohne eine Kategorie? Dies geschieht sehr oft: Ihre Kategorie Abschnitt enthält einige Kategorie orientierte Widgets, während die Artikel Abschnitt einige Artikel bezogenen Widgets und Filter. Auch Ihre Artikel Controller hat einige spezielle before_filters Sie vielleicht nutzen wollen, aber Sie haben sie nicht zu verwenden, wenn der Artikel-Eintrag zu einer Kategorie gehört.
In diesem Fall möchten Sie vielleicht 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 in der Regel, weil meine bevorzugte Wahl mit einer zusätzlichen Aktion müssen Sie Ihre Ansichten und Controller mit Tonnen von bedingten Kontrollen nicht unübersichtlich.
Andere Tipps
Ich mag oft diese Aktionen zu trennen. Wenn die daraus resultierenden Maßnahmen sehr ähnlich sind, können Sie die Bereiche in der Steuerung durch den Anblick leicht trennen, wenn params. [: Category_id] vorhanden ist usw. (siehe @SimoneCarletti Antwort)
Normalerweise Aktionen in der Steuerung zu trennen, indem individuelle Routen mit bietet Ihnen ein Höchstmaß an Flexibilität und klare Ergebnisse. Folgende Code führt in Normalweg Helfer Namen, aber die Routen zu bestimmten Aktionen in der Steuerung gerichtet sind.
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
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
Mit nur eine einzige verschachtelte Ressource, eine Bedingung, dass die params basierend Verwendung zu bestimmen ist Umfang der einfachste Ansatz wäre. Dies ist wahrscheinlich die Art und Weise, in Ihrem Fall zu gehen.
if params[:category_id]
@articles = Category.find(params[:category_id]).articles
else
@articles = Article.all
end
Allerdings, je nachdem, welche andere verschachtelten Ressourcen, die Sie für das Modell haben, mit diesem Ansatz stecken kann ziemlich mühsam bekommen. In diesem Fall mit einem Plugin wie resource_controller oder make_resourceful wird einfacher machen dies viel.
class ArticlesController < ResourceController::Base
belongs_to :category
end
Dies wird tatsächlich tun alles, was man erwarten würde. Es gibt Ihnen alle Ihre Standard RESTful Aktionen und automatisch Setup den Spielraum für /categories/1/articles
.
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 zu gelangen, macht eine vernünftige Entscheidung, was zu tun ist.