タイトルのとおりなんですが、ArticleComment とかあったりして、ちゃんと設定をしておくと article.comments とやると あるArticleに紐づいているCommentがとってこれる機能です。

まず、結論からいうと article.comments.to_sql とか article.comments.scoped とか article.comments.joinsとかできる!! ということです。

article.comments.create ってかける時点でうすうす思ってたんですが、これがわかっていると小回りがききます。返しているものが ActiveRecord::Relationのようなものです。classを確認すると Arrayって言われちゃいますが。

もうちょい深く

以下のクラスがあることを想定してみます。

class Article < ActiveRecord::Base
  has_many :comments
end
class Comment < ActiveRecord::Base
  belongs_to :artcile
  belongs_to :user
end
class User
  has_many :comment
end

さきほど紹介した技を紹介すると User.first かつ Article.first な Commentを探す場合、以下のように書けます

a = Article.first
u = User.first
a.comments.merge(u.comments.scoped)

すると、こんな SQLができます。

SELECT "comments".* FROM "comments"  WHERE "comments"."article_id" = 1 AND "comments"."user_id" = 1

aとかuとかを引数な関数を用意するとウハウハな気がしてこないでしょうか。

joinだってできます。

a = Article.first
a.comments.merge(Comment.joins(:user))
SELECT "comments".* FROM "comments" INNER JOIN "users" ON "users"."id" = "comments"."user_id" WHERE "comments"."article_id" = 1

これは has_many through でもできますね。

このあたりを上手くつかっていけば ActiveRecordでも作りたいSQLがある程度つくれるんじゃないでしょうか。

Rails4がくると scoped をかかなくてもよくなるような気がしますが、試していません。

しかし、はじめの例ですが、

a.comments.where(user_id: u)

「ってかいても同じじゃね?」とか、言わないでください。なんとなく user_id ってかきたくなくないですか?

なんとなくおまけ

a.comments とかいた場合は Commentの Relationをつくっている。

u.comments とかいても Commentの Relationをつくっている。

と、メソッド名のほうのテーブルを意識してやると理解しやすいと思います。

レシーバほうに対してのテーブルを意識すると息苦しくなります。has_manyをかく場合はそういう意識になるのでちょっと注意が必要です。

ちょんと感覚的な話でした。

サンプル用コード

動作確認のためのコードを用意しておきました。おすきにお使いください。

https://github.com/eiel/has_many-relation