LoginSignup
8
5

More than 5 years have passed since last update.

has_many :throughの結合モデルの属性を関連先モデルの属性として取得する

Last updated at Posted at 2017-07-28

やること

あるユーザが画像群の中から複数枚を選んでプロフィール画像として設定できるような、次のテーブルがあるとします。特徴としては、結合モデル profile_images に外部キー以外の属性 order があることが挙げられます。

relations.png

これをActive Recordで書くと次のようになります。

class User < ApplicationRecord
  has_many :profile_images
  has_many :images, through: :profile_images
end

class ProfileImage < ApplicationRecord
  belongs_to :users
  belongs_to :images
end

# Image は省略

このとき、結合モデル ProfileImage の属性 order を取得する場合と、関連先のモデル Image の属性 src を取得する場合とで、次のように書き分けなければなりません。

user.profile_images.first.order
user.images.first.src

記述を統一させるために、User のレコードから関連先 Image をたどる場合は、次のように ProfileImage の属性も Image の属性も統一した形で取得できるようにします。

user.ordered_images.first.order # User からたどると Image から ProfileImage#order を参照できる
user.ordered_images.first.src

やりかた

has_many の第2引数 (scope) に次のようなラムダ式を渡します。この引数は関連先を取得するときに使うクエリををカスタマイズするために使います。

class User < ApplicationController
  has_many :ordered_images,
           ->{ select("#{Image.table_name}.*", "#{ProfileImage.table_name}.order AS order") },
           through: :profile_images,
           class_name: 'Image'
end

※注意: ここでは普通の関連と区別するために ordered_images という名前にしています。また、この方法を使うと、user.ordered_images.count のような集約操作はクエリをうまく生成できずエラーになります。

ここでは、テーブル users, profile_images, images をJOINしたテーブルをもとに関連先 Image のオブジェクト群を取得するときに、ラムダ式内の selectImage の属性に加えて ProfileImage.order を合わせてSELECTするようにしています。

これで user.ordered_images.first.order のように、User レコードから関連をたどったときは関連先モデルが結合モデルの属性を持つかのように取り扱うことができます。

参考資料

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5