Engineering

Feb 14, 2018

polymophicなテーブルを経由して、has_many thourghする。

 

Railsのアソシエーションで、中間テーブルを経由して一個飛ばしでインスタンスを取得できるhas_many~through便利ですよね。ところが、この中間テーブルがpolymophicだった場合に、どうやって対象を取得すれば良いのかということに悩みました。

 

解決策は、結論からいうと、下の例でいうと モデルにはこういう形で定義して、そのままuser.post_commentsのような形でできます。

 

 

has_many :post_comments, through: :comments, :source => :commentable,:source_type => 'Post'
has_many :blog_comments, through: :comments :source => :commentable,:source_type => 'Blog'

 

 

図やコードを交えて解説

 

これだけではなんのことかよくわからないかもしれないので、上の結論のコードの全容を明かすと以下のようになります。

以下のモデルは、複数のユーザがブログを作り、各々ポストを投げていくという世界を想定したモデル群です。

polymophic-through-1024x767.png

 

 

class User < ActiveRecord::Base
  has_many :blogs
  has_many :posts
  has_many :comments
  has_many :post_comments, through: :comments, :source => :commentable,:source_type => 'Post'
  has_many :blog_comments, through: :comments :source => :commentable,:source_type => 'Blog'
end

class Blog < ActiveRecord::Base
  has_many :comments, as: :commentable
end

class Post < ActiveRecord::Base
  has_many :comments, as: :commentable
end

class Comment < ActiveRecord::Base
  belongs_to :commentables, polymophic: true
end

 

コードを見てみると、確かにいつものようにhas_manyで「Userが行った投稿を取ってくる」ことや「Userが持っているブログを取得する」ということは簡単にできます。ただ、「ユーザーがコメントした投稿」「ユーザーがコメントしたブログ」についてはどうやって取得しようか少し迷うところがあると思います。

 

 

特に、ここでは、中間テーブルの役割をはたすCommentがポリモーフィック関連なのでなおさらです。

railsのポリモーフィック関連では、able_idとable_typeというカラムで関連が定義されますが、上のコードを例にとるとhas_many :commentsだけでは、「ユーザーがコメントした投稿」と「ユーザーがコメントしたブログ」の両方ともが取得されてきてしまいます。愚直にやるのであれば、commentable_typeを条件にSQLを発行してとやればできなくはないですが、それは苦しいです。。

 

 

そこで、この解決策では、:sourceオプションと:source_typeオプションを使って、ポリモーフィック関連の先のテーブルを指定してあげることで「ユーザーがコメントした投稿」と「ユーザーがコメントしたブログ」を簡単に取得できるようにしています。

 

polymophicなテーブルを経由して、has_many thourghする場合は、sourceオプションでポリモーフィック関連の名前を指定して、source_typeで中間テーブルの先のクラスを指定してシンプルにデータをとってきましょう。

 

 

関連記事

記事検索

気になるサイト内の記事を検索する

プロフィール

バンクーバー在住のフルスタックエンジニアです。React, Ruby on Rails, Go などでお仕事しています。職場がトロントなので日本、トロント、バンクーバーの三つの時天空を操って生活しています。

プロモーション