具有多个角色的人的设计模式

我想创建一个基本模型Person ,其中包含一些与人物相关的属性,如姓名,地址,电话等。 一个Person可以是以下一种或多种

  • LoginUser包含登录,密码,last_login,…的字段
  • CardHolder ,包含card_id,last_entrance,…的字段
  • 只有一个标志的Supplier ,无论该人是否是供应商
  • 无论该人是否是Recipient ,只Recipient一个标志的收件人

Ruby on Rails中是否存在常识或最佳实践设计模式来表示inheritance? 它应该如何在模型和表结构中表示,以便可以检查Person是否是LoginUser并访问相应的字段。 在另一个项目中,我已经与STI合作,但在这种情况下,这不是正确的模式。

您正在寻找的是反向多态关联。 多态关联允许您将一个模型链接到许多不同的模型。 反向多态关联允许您将多个模型链接到一个模型。 它们设置起来有点棘手,但是一旦掌握了它就没问题了。

为了实现这一点,您需要另一个模型作为Person模型和每个不同角色的中间人。 这种中间模型实际上具有多态关联。 您的Person模型将具有该模型,并且您的各种角色模型将具有该模型。 然后使用:through来完成其余的必要关联,这样你的代码就不会有任何不同了。 快变!

以下是如何使用PersonCardHolder模型执行此操作的示例。 我正在调用额外的模型Role因为这似乎是一个明显的选择:

 class Person < ApplicationRecord has_many :roles # Reach through the Roles association to get the CardHolders, via polymorphic :rollable. # Unfortunately, you can't has_one, so you'll have to enforce uniqueness in Role # with a validation. has_many :card_holders, through: :roles, source: :rollable, source_type: 'CardHolder' end class Role < ApplicationRecord belongs_to :person # Here is where our actual polymorphic connection is: belongs_to :rollable, polymorphic: true end class CardHolder < ApplicationRecord # The other side of the polymorphic connection, with has_one: has_one :role, as: :rollable # Get the person via the role, just like the inverse: has_one :person, through: :role end 

数据库设置如下:

 class CreatePeople < ActiveRecord::Migration[5.1] def change create_table :people do |t| t.string :name # put in whatever other Person columns you need t.timestamps end end end class CreateRoles < ActiveRecord::Migration[5.1] def change create_table :roles do |t| t.references :person, index: true t.references :rollable, polymorphic: true, index: true t.timestamps end end end class CreateCardHolders < ActiveRecord::Migration[5.1] def change create_table :card_holders do |t| t.integer :card_id t.datetime :last_entrance # put in whatever other columns you need t.timestamps end end end 

使用它很简单:

 > p = Person.create(name: "Sven Reuter") # directly add a card holder > p.card_holders << CardHolder.create(card_id: 1, last_entrance: Time.current) # build a role instead > p.roles.build(rollable: CardHolder.new(card_id: 2, last_entrance: Time.current) # get all of the roles > p.roles 

我会使用Person表和PersonAttributes表,该表是该人可能具有的所有属性的并集。 如果适用, PersonAttributes可能使用STI,例如,使用存储登录的LoginUser和引用CardCardHolder

干净简单。