Techioz Blog

多態性クラスから関連クラスへの関連付けを取得するにはどうすればよいですか

概要

いくつかのポリモーフィックな関連付けを適切に機能させようとしていますが、正しく動作させることができません。現在、直接の関係ではなく、両方のテーブルに一致する ID がすべて提供されます。

class User < ApplicationRecord
  belongs_to :origin, polymorphic: true
  has_one :customer, foreign_key: :id, primary_key: :origin_id, class_name: "Customer"
  has_one :employee, foreign_key: :id, primary_key: :origin_id, class_name: "Employee"
end

class Customer < ApplicationRecord
  has_one :user, class_name: "User", foreign_key: :origin_id
end

class Employee < ApplicationRecord
  has_one :user, class_name: "User", foreign_key: :origin_id
end

私が行った場合:

user = User.create(origin: Customer.find(1))
user.customer # => #<Customer:0x000000014a78d2c8>
# I expect that the code below to be nil but it is not
user.employee # => #<Employee:0x000000014a78d2c8>

適切な関連付けを取得する方法を知っている人はいますか?前もって感謝します。

解決策

Rails ガイドには、 has_many のポリモーフィックな関連付けの例があります

has_one にはほぼ同じコードがあります。belongs_to 関連付けオプションについてもお読みください。

class User < ApplicationRecord
  belongs_to :origin, polymorphic: true

  belongs_to :customer, -> { where(users: { origin_type: 'Customer' }).includes(:user) }, foreign_key: :origin_id
  belongs_to :employee, -> { where(users: { origin_type: 'Employee' }).includes(:user) }, foreign_key: :origin_id

  # Or simply define instance methods if you don't need above associations:
  # def customer
  #   origin if origin_type == 'Customer'
  # end

  # def employee
  #   origin if origin_type == 'Employee'
  # end
end

class Customer < ApplicationRecord
  has_one :user, as: :origin
end

class Employee < ApplicationRecord
  has_one :user, as: :origin
end

いずれの場合も、そのようなスキーマの移行が必要です。

create_table :users do |t| # or change_table if it is existing table
  t.belongs_to :origin, polymorphic: true # origin_id and origin_type columns
end

出力は次のようになります

user = User.create(origin: Customer.find(1))
user.customer # => #<Customer:0x000000014a78d2c8>
user.employee # => nil