简体   繁体   English

无法让STI充当模型上的多态关联

[英]Can't get STI to act as polymorphic association on model

I have a User model that can have an email and a phone number, both of which are models of their own as they both require some form of verification. 我有一个可以包含电子邮件和电话号码的用户模型,这两个模型都是它们自己的模型,因为它们都需要某种形式的验证。

So what I'm trying to do is attach Verification::EmailVerification as email_verifications and Verification::PhoneVerification as phone_verifications , which are both STIs of Verification . 所以我想做的是将Verification::EmailVerification附加为email_verifications并将Verification::PhoneVerificationphone_verifications ,它们都是Verification STI。

class User < ApplicationRecord
  has_many :email_verifications, as: :initiator, dependent: :destroy
  has_many :phone_verifications, as: :initiator, dependent: :destroy

  attr_accessor :email, :phone

  def email
    @email = email_verifications.last&.email
  end

  def email=(email)
    email_verifications.new(email: email)
    @email = email
  end

  def phone
    @phone = phone_verifications.last&.phone
  end

  def phone=(phone)
    phone_verifications.new(phone: phone)
    @phone = phone
  end
end

class Verification < ApplicationRecord
  belongs_to :initiator, polymorphic: true
end

class Verification::EmailVerification < Verification
  alias_attribute :email, :information
end

class Verification::PhoneVerification < Verification
  alias_attribute :phone, :information
end

However, with the above setup I get the error uninitialized constant User::EmailVerification . 但是,通过上述设置,我得到了uninitialized constant User::EmailVerification的错误。 I'm unsure of where I'm going wrong. 我不确定我要去哪里。

How I structure this so that I can access email_verifications and phone_verifications on the User model? 如何构造此结构,以便可以在用户模型上访问email_verificationsphone_verifications

When using STI you don't need (or want) polymorphic associations. 使用STI时,您不需要(或想要)多态关联。

Polymorphic associations are a hack around the object-relational impedance mismatch problem used to setup a single association that points to multiple tables. 多态关联是围绕对象关系阻抗不匹配问题的黑客,这些对象关系阻抗不匹配问题用于设置指向多个表的单个关联。 For example: 例如:

class Video
  has_many :comments, as: :commentable
end

class Post
  has_many :comments, as: :commentable
end

class Comment
  belongs_to :commentable, polymorphic: true
end

The reason they should be used sparingly is that there is no referential integrity and there are numerous problems related to joining and eager loading records which STI does not have since you have a "real" foreign key column pointing to a single table. 应当谨慎使用它们的原因是,没有参照完整性,并且存在很多与联接和渴望加载记录相关的问题,而STI没有这些记录,因为您有指向单个表的“真实”外键列。

STI in Rails just uses the fact that ActiveRecord reads the type column to see which class to instantiate when loading records which is also used for polymorphic associations. Rails中的STI仅使用ActiveRecord读取type列来查看在加载记录时实例化哪个类的事实,该记录也用于多态关联。 Otherwise it has nothing to do with polymorphism. 否则,它与多态无关。

When you setup an association to a STI model you just have to create an association to the base inheritance class and rails will handle resolving the types by reading the type column when it loads the associated records: 当您设置与STI模型的关联时,您只需要创建与基本继承类的关联,并且Rails将在加载关联记录时通过读取type列来处理解析类型:

class User < ApplicationRecord
  has_many :verifications
end

class Verification < ApplicationRecord
  belongs_to :user
end

module Verifications 
  class EmailVerification < ::Verification
    alias_attribute :email, :information
  end
end

module Verifications 
  class PhoneVerification < ::Verification
    alias_attribute :email, :information
  end
end

You should also nest your model in modules and not classes. 您还应该将模型嵌套在模块而不是类中。 This is partially due to a bug in module lookup that was not resolved until Ruby 2.5 and also due to convention. 这部分是由于模块查找中的错误直到Ruby 2.5才解决,也归因于约定。

If you then want to create more specific associations to the subtypes of Verification you can do it by: 然后,如果您想与Verification的子类型建立更具体的关联,则可以通过以下方式实现:

class User < ApplicationRecord
  has_many :verifications
  has_many :email_verifications, ->{ where(type: 'Verifications::EmailVerification') },
          class_name: 'Verifications::EmailVerification'
  has_many :phone_verifications, ->{ where(type: 'Verifications::PhoneVerification') },
          class_name: 'Verifications::PhoneVerification'
end

If you want to alias the association user and call it initiator you do it by providing the class name option to the belongs_to association and specifying the foreign key in the has_many associations: 如果你想别名关联user ,并调用它initiator你做它通过提供类名选项将belongs_to关联,并指定在该外键has_many协会:

class Verification < ApplicationRecord
  belongs_to :initiator, class_name: 'User'
end

class User < ApplicationRecord
  has_many :verifications, foreign_key: 'initiator_id'
  has_many :email_verifications, ->{ where(type: 'Verifications::EmailVerification') },
          class_name: 'Verifications::EmailVerification',
          foreign_key: 'initiator_id'
  has_many :phone_verifications, ->{ where(type: 'Verifications::PhoneVerification') },
          class_name: 'Verifications::PhoneVerification',
          foreign_key: 'initiator_id'
end

This has nothing to do with polymorphism though. 但是,这与多态无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM