ActiveRecord#joinsメソッド覚えました

Rails4

joinsメソッドを最近覚えました。
ActiveRecord#joinsとは、複数のテーブルを結合するメソッドです。
テーブルを跨いでレコードの抽出をしたい時に使います。

whereでin句を使えば、テーブル結合しなくても同じ結果が得られるのですがコードが冗長になると思います。(後述)

joinsメソッドを使った例は、ぺろぺろあんてなで使ったコードで説明してきます。
このアンテナサイト では、メインコンテンツ部に『サイトカテゴリ毎の二日分記事』を表示しております。
http://prpr-antena.com/adult であれば、アダルトカテゴリに属するサイトの記事を表示し、
http://prpr-antena.com/vipであれば、VIPカテゴリに属するサイトの記事を表示しています。

まずモデルの関連

モデルの関連は以下ようになっています。
記事はサイトに属していて、サイトはカテゴリに属しています。

class Post < ActiveRecord::Base                                                                                                                               
  belongs_to :site
class Site < ActiveRecord::Base                                                                                                                               
  has_many :posts
  belongs_to :category
end
class Category < ActiveRecord::Base
  has_many :sites
[Post]*--[Site]*--[Category]

コントローラー

メインコンテンツ部に表示する記事は、”カテゴリ”で抽出したいのですが、
Postモデルだけでは、自身がどのサイトカテゴリに属しているかわかりません。
ということは、postsテーブルとsitesテーブルを結合する必要があります。
ちなみに、パラメータにはcategoriesテーブルのidが渡ってきます。

sitesテーブルが持っているcategoryの外部キーを使っても目標達成することはできるので、
categoriesテーブルとの結合は必須ではないのですが、今回は一緒に結合してみました。

以下のようになります。

Post.joins(site: :category).where("categories.id = ?", 2)

postsテーブルとsitesテーブルに結合したsitesテーブルにcategoriesテーブルを結合しています。

SELECT "posts".* FROM "posts" INNER JOIN "sites" ON "sites"."id" = "posts"."site_id" INNER JOIN "categories" ON "categories"."id" = "sites"."category_id" WHERE (categories.id = 2)

joinsメソッド使ってないのと比較してみる

使ってない
@category = Category.find(params[:id])
ids = @category.sites.pluck(:id)
@posts = Post.where("site_id in (?)", ids).
  where("posts.created_at >= ?", DateTime.now - 2.day).
  order("created_at DESC")
使った
@category = Category.find(params[:id])
@posts =  Post.joins(site: :category).where("categories.id = ?", @category).
  where("posts.created_at >= ?", DateTime.now - 2.day).
  order("created_at DESC")

DBへの問い合わせが1回減りました。

ActiveRecord#joinsメソッド覚えました。
http://guides.rubyonrails.org/active_record_querying.html