Rails 5.2 ポリモーフィック条件付きのポリモーフィックな関連付け

概要

class Car < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Truck < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Listing < ApplicationRecord
  belongs_to :listable, polymorphic: true
  has_many :favorites, dependent: :destroy
  has_many :users_who_favorited, through: :favorites, source: :user
end

そして、Car と Truck の両方に user_id フィールドがあると仮定します。

Listing.includes(:listable) は、eargerloaded リストの AR 関係を返します。

ただし、user_idでフィルタリングする必要があるので、試してみました…

Listing.includes(:listable).where(user_id: 100)

しかし、エラーが発生します..

リストで user_id を検索しているようです。ただし、listables テーブルでフィルターする必要があるため、Car テーブルまたは Truck テーブルのいずれかを意味します。ただし、listable は定義されています。

私も試してみました:

Listing.includes(listable:[:user]).where(‘users.id = 100’)

でも分かりました…

それから試してみました

class Listing < ApplicationRecord
  belongs_to :listable, polymorphic: true
  has_many :favorites, dependent: :destroy
  has_many :users_who_favorited, through: :favorites, source: :user

  belongs_to :car, -> { includes(:listable).where(listable: { listable_type: Car.to_s }) }, foreign_key: :listable_id
  belongs_to :truck, -> { includes(:listable).where(listable: { listable_type: Truck.to_s }) }, foreign_key: :listable_id

end

Listing.includes(:car, :truck) を試しましたが、得られました。

したがって、上記が機能するまで、以下を試すことはできません。

Listing.includes(:car, :truck).where(cars: { user_id: 1 }).or(Listing.includes(:car, :truck).where(trucks: { user_id: 1 }))

ただし、Listing.includes(:listable) を実行すると機能しますが、条件を追加すると壊れます。

解決策

これは私が数か月間抱いていた非常に興味深い質問です。それから私はそれに対する解決策を見つけました。

Listing モデルにポリモーフィック モデルを含めることができるようにするには、それらが関連していることをモデルに伝える必要があります。

class Car < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Truck < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Listing < ApplicationRecord
  belongs_to :listable, polymorphic: true
  has_many :favorites, dependent: :destroy
  has_many :users, through: :favorites

  #magic happens here
  belongs_to :car, -> { includes(:listings).where(listings: { listable_type: Car.to_s }) }, foreign_key: :listable_id
  belongs_to :truck, -> { includes(:listings).where(listings: { listable_type: Truck.to_s }) }, foreign_key: :listable_id

end

これで、単純に Listing.includes(:car, :truck) を行うことができ、完全に動作します :-)

あなたの場合:

Listing.includes(:car, :truck).where(cars: { user_id: 1 }).or(Listing.includes(:car, :truck).where(trucks: { user_id: 1 }))