Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

やること

あるユーザが画像群の中から複数枚を選んでプロフィール画像として設定できるような、次のテーブルがあるとします。特徴としては、結合モデル 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 レコードから関連をたどったときは関連先モデルが結合モデルの属性を持つかのように取り扱うことができます。

参考資料

pepabo
「いるだけで成長できる環境」を標榜し、エンジニアが楽しく開発できるWebサービス企業を目指しています。
https://pepabo.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした