すでに述べたように、アクティブなレコードの関連付けは、便利なメソッドのメトリクス バットロードを作成します。もちろん、独自のメソッドを記述してすべてをフェッチすることもできます。しかし、それは Rails Way ではありません。
Rails Way は 2 つのモットーの集大成です。DRY (Don't Repeat Yourself) と「Convention over Configuration」。基本的に、意味のある名前を付けることにより、フレームワークによって提供されるいくつかの堅牢なメソッドは、すべての共通コードを抽象化できます。質問に配置したコードは、単一のメソッド呼び出しで置き換えることができるものの完璧な例です。
これらの便利なメソッドが本当に役立つのは、より複雑な状況です。結合モデル、条件、検証などに関連するもの。
のようなことをするときの質問に答えるために@user.articles.find(:all, :conditions => ["created_at > ? ", tuesday])
、Rails は 2 つの SQL クエリを準備し、それらを 1 つにマージします。あなたのバージョンはオブジェクトのリストを返すだけです。名前付きスコープも同じことを行いますが、通常はモデルの境界を越えません。
コンソールでこれらのことを呼び出すときに、development.log で SQL クエリをチェックすることで検証できます。
RailsがSQLを処理する方法の素晴らしい例を提供するので、名前付きスコープについて少し話しましょう.見せびらかす。
名前付きスコープを使用して、モデルのカスタム検索を実行できます。これらは連鎖させることも、関連付けを介して呼び出すこともできます。同一のリストを返すカスタム ファインダーを簡単に作成できますが、質問で述べたのと同じ問題に遭遇します。
class Article < ActiveRecord::Base
belongs_to :user
has_many :comments
has_many :commentators, :through :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
Article.edited_scope
=> # Array of articles that have been flagged as edited. 1 SQL query.
Article.edited_method
=> # Array of Articles that have been flagged as edited. 1 SQL query.
Array.edited_scope == Array.edited_method
=> true # return identical lists.
Article.recent_scope
=> # Array of articles that have been updated in the past 7 days.
1 SQL query.
Article.recent_method
=> # Array of Articles that have been updated in the past 7 days.
1 SQL query.
Array.recent_scope == Array.recent_method
=> true # return identical lists.
変更点は次のとおりです。
Article.edited_scope.recent_scope
=> # Array of articles that have both been edited and updated
in the past 7 days. 1 SQL query.
Article.edited_method.recent_method
=> # no method error recent_scope on Array
# Can't even mix and match.
Article.edited_scope.recent_method
=> # no method error
Article.recent_method.edited_scope
=> # no method error
# works even across associations.
@user.articles.edited.comments
=> # Array of comments belonging to Articles that are flagged as
edited and belong to @user. 1 SQL query.
基本的に、名前付きスコープごとに SQL フラグメントが作成されます。Rails は、チェーン内の他のすべての SQL フラグメントと巧みにマージして、必要なものを正確に返す単一のクエリを生成します。関連メソッドによって追加されたメソッドは、同じように機能します。これが、named_scopes とシームレスに統合される理由です。
mix & match が機能しなかった理由は、質問で定義された of_sector メソッドが機能しないことと同じです。edit_methods は配列を返します。ここで、edited_scope (チェーンの一部として呼び出される find および他のすべての AR 便利なメソッドと同様) は、SQL フラグメントをチェーン内の次のものに渡します。チェーンの最後の場合は、クエリを実行します。同様に、これも機能しません。
@edited = Article.edited_scope
@edited.recent_scope
このコードを使用しようとしました。これを行う適切な方法は次のとおりです。
class User < ActiveRecord::Base
has_many :articles do
def of_sector(sector_id)
find(:all, :conditions => {:sector_id => sector_id})
end
end
end
この機能を実現するには、次のようにします。
class Articles < ActiveRecord::Base
belongs_to :user
named_scope :of_sector, lambda do |*sectors|
{ :conditions => {:sector_id => sectors} }
end
end
class User < ActiveRecord::Base
has_many :articles
end
次に、次のようなことができます。
@user.articles.of_sector(4)
=> # articles belonging to @user and sector of 4
@user.articles.of_sector(5,6)
=> # articles belonging to @user and either sector 4 or 5
@user.articles.of_sector([1,2,3,])
=> # articles belonging to @user and either sector 1,2, or 3