Wie bereits erwähnt, erstellen die aktiven Datensatzverbände eine ganze Menge an praktischen Methoden. Sicher, Sie könnten Ihre eigenen Methoden schreiben, um alles abzurufen. Aber das ist nicht der Rails-Weg.
Der Rails-Weg ist das Ergebnis von zwei Leitsätzen. DRY (Don't Repeat Yourself) und "Konvention vor Konfiguration". Im Grunde genommen können durch Benennung von Dingen auf eine sinnvolle Weise einige robuste Methoden, die vom Framework bereitgestellt werden, den ganzen gemeinsamen Code abstrahieren. Der Code, den Sie in Ihrer Frage platzieren, ist das perfekte Beispiel für etwas, das durch einen einzigen Methodenaufruf ersetzt werden kann.
Wo diese praktischen Methoden wirklich glänzen, sind die komplexeren Situationen. Die Art von Dingen, die Verbindungsmodelle, Bedingungen, Validierungen usw. beinhalten.
Um Ihre Frage zu beantworten, wenn Sie etwas wie @user.articles.find(:all, :conditions => ["created_at > ?", Dienstag])
machen, bereitet Rails zwei SQL-Abfragen vor und verschmilzt sie dann zu einer. Wohingegen Ihre Version einfach die Liste der Objekte zurückgibt. Benannte Scopes tun dasselbe, überschreiten aber normalerweise keine Modellgrenzen.
Sie können dies überprüfen, indem Sie die SQL-Abfragen in der development.log überprüfen, während Sie diese Dinge in der Konsole aufrufen.
Lassen Sie uns also einen Moment über benannte Scopes sprechen, weil sie ein großartiges Beispiel dafür geben, wie Rails mit dem SQL umgeht, und ich denke, sie sind eine einfachere Möglichkeit zu zeigen, was hinter den Kulissen passiert, da sie keine Modellverbände benötigen, um sie zu zeigen.
Benannte Scopes können verwendet werden, um benutzerdefinierte Suchen eines Modells durchzuführen. Sie können verkettet oder sogar durch Verbände aufgerufen werden. Sie könnten problemlos benutzerdefinierte Suchfunktionen erstellen, die identische Listen zurückgeben, aber dann stoßen Sie auf die gleichen Probleme wie in der Frage erwähnt.
class Artikel < ActiveRecord::Base
gehört zu :user
hat viele :comments
hat viele :commentators, :durch :comments, :class_name => "user"
named_scope :edited_scope, :conditions => {:edited => true}
named_scope :recent_scope, lambda do
{ :conditions => ["updated_at > ?", DateTime.now - 7.days]}
def self.edited_method
self.find(:all, :conditions => {:edited => true})
end
def self.recent_method
self.find(:all, :conditions => ["updated_at > ?", DateTime.now - 7 days])
end
end
Artikel.edited_scope
=> # Array von Artikeln, die als bearbeitet gekennzeichnet wurden. 1 SQL-Abfrage.
Artikel.edited_method
=> # Array von Artikeln, die als bearbeitet gekennzeichnet wurden. 1 SQL-Abfrage.
Array.edited_scope == Array.edited_method
=> true # gibt identische Listen zurück.
Artikel.recent_scope
=> # Array von Artikeln, die in den letzten 7 Tagen aktualisiert wurden. 1 SQL-Abfrage.
Artikel.recent_method
=> # Array von Artikeln, die in den letzten 7 Tagen aktualisiert wurden. 1 SQL-Abfrage.
Array.recent_scope == Array.recent_method
=> true # gibt identische Listen zurück.
Hier ändern sich die Dinge:
Artikel.edited_scope.recent_scope
=> # Array von Artikeln, die sowohl bearbeitet wurden als auch in den letzten 7 Tagen aktualisiert wurden. 1 SQL-Abfrage.
Artikel.edited_method.recent_method
=> # Fehler bei der Methode recent_scope auf Array
# Man kann nicht einmal mischen und kombinieren.
Artikel.edited_scope.recent_method
=> # Fehler bei der Methode
Artikel.recent_method.edited_scope
=> # Fehler bei der Methode
# Funktioniert sogar über Verbände hinweg.
@user.articles.edited.comments
=> # Array von Kommentaren, die zu Artikeln gehören, die als bearbeitet markiert sind und zu @user gehören. 1 SQL-Abfrage.
Prinzipiell erstellt jeder benannte Scope ein SQL-Fragment. Rails wird geschickt jedes andere SQL-Fragment in der Kette mit jedem anderen verschmelzen, um eine einzige Abfrage zu erzeugen, die genau das zurückgibt, was Sie wollen. Die von den Verbandsmethoden hinzugefügten Methoden funktionieren genauso. Daher integrieren sie sich nahtlos in benannte Scopes.
Der Grund, warum das Mischen und Kombinieren nicht funktioniert hat, ist derselbe wie der, dass die in der Frage definierte of_sector-Methode nicht funktioniert. edited_methods gibt ein Array zurück, während edited_scope (sowie find und alle anderen AR-Bequemlichkeitsmethoden, die als Teil einer Kette aufgerufen werden) ihr SQL-Fragment an die nächste Sache in der Kette weitergeben. Wenn es das letzte in der Kette ist, führt es die Abfrage aus. Ähnlich funktioniert dies auch nicht.
@edited = Artikel.edited_scope
@edited.recent_scope
Sie haben versucht, diesen Code zu verwenden. Hier ist der richtige Weg, dies zu tun:
class Benutzer < ActiveRecord::Base
hat viele :articles do
def of_sector(sector_id)
find(:all, :conditions => {:sector_id => sector_id})
end
end
end
Um diese Funktionalität zu erreichen, möchten Sie dies tun:
class Artikel < ActiveRecord::Base
gehört zu :user
named_scope :of_sector, lambda do |*sectors|
{ :conditions => {:sector_id => sectors} }
end
end
class Benutzer < ActiveRecord::Base
hat viele :articles
end
Dann können Sie Dinge wie diese tun:
@user.articles.of_sector(4)
=> # Artikel, die zu @user gehören und im Sektor 4 sind
@user.articles.of_sector(5,6)
=> # Artikel, die zu @user gehören und entweder im Sektor 4 oder 5 sind
@user.articles.of_sector([1,2,3,])
=> # Artikel, die zu @user gehören und entweder im Sektor 1,2 oder 3 sind