【Rails】多対多のアソシエーションに別名をつけたいあなたに
最近涼しくなってきましたねー…と思ったら次の日は暑かったりで体を壊しそうになります。(2週間前にすでに壊した)
今日は最近コードをレビューする機会があって、多対多のアソシエーションをうまいこと設定できていなくて指摘したかったものの、どうやって設定するか微妙に調べることになったのでもう忘れないようにブログにしておこうと思った次第。
想定
会員がブログを投稿できる機能があって、他の人が書いたブログをお気に入りできるみたいな機能を作る想定で
以下のような感じのテーブル構成をイメージ
とりあえずわかりやすいところから
各モデルのアソシエーションのとりあえずわかりやすいところから記載
# user.rb class User < ApplicationRecord has_many :blogs has_many :favorites end # blog.rb class Blog < ApplicationRecord belongs_to :user has_many :favorites has_many :users, through: :favorites end # favorite.rb class Favorite < ApplicationRecord belongs_to :user belongs_to :blog end
多対多の関連がある時はthrough
オプションをつけてあげると中間テーブルを経由して関連先のモデルを取得できるようになります。
through
をつけないと中間テーブルの先のレコードを取得するのが一手間必要になり微妙なので設定するのが吉です。
# blogからuserを取得する例 # through設定した場合 blog.users # through設定しない場合 blog.favorites.map { |favorite| favorite.user }
設定しない場合の取得方法はあくまで例ですが何かしら下処理が必要になってしまいます…
で、それならuserにもthroughを設定してblogを取得できるようにしたらいーじゃん!ってなりますよね。
ただ今回はblogとuserにはすでにアソシエーションが存在します。
こんな時は別名をつけてあげれば良いのです!
多対多で中間テーブルの先のモデルとアソシエーションする
こんな感じで実現できます。
# user.rb class User < ApplicationRecord has_many :blogs has_many :favorites has_many :favorite_blogs, through: :favorites, source: :blog end
source
オプションを設定することでuser.favorite_blogs
でuserがお気に入りしたブログを一気に取得することができます。
Railsは外部キーの名前などでアソシエーションの判断をしていますが、アソシエーションの名前を外部キーの名前から外れた名前にする場合はそれをオプションをつけて教えてあげる必要があります。
source
オプションがthroughする際にアクセスすべきモデルを教えるオプションとなります。
source: :blog
とすることでfavorite.rb
で設定しているbelongs_to :blog
を参照するようになり、favorite_blogs
というblog_id
に紐づくという判断ができない名前であってもblog.rbに辿り着けるようになるといった感じです!(説明わかりづらい説)
なのでこのようなアソシエーションを作成したいとなった場合はsource
オプションを活用しましょう!
ちなみに1対多で違った名前を付ける際の対処
たとえば何らかの理由でhas_many :blogs
をhas_many :write_blogs
にしなければいけなくなった際は
class User has_many :write_blogs, class_name: 'Blog', foreign_key: :user_id # 以下省略 end
みたいな感じで、このアソシエーションはどこのモデルとのアソシエーションなのか、どの外部キーを用いるのかをオプションで明示する必要があります。
最後に
NEW GAMEを見てるとみんな才能があって素敵に思う。
ねねっちを弊社にスカウトしたいのだけどどこで出会えるのかしら