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

时间:2017-11-16 10:42:57

标签: ruby-on-rails ruby design-patterns model

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

  • LoginUser包含登录名,密码,last_login,...
  • CardHolder包含card_id,last_entrance,...
  • 的字段
  • Supplier只是一个标志,无论该人是否是供应商<​​/ li>
  • Recipient只带一个标记,无论该人是否为收件人

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

2 个答案:

答案 0 :(得分:1)

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

为了实现这一目标,您需要另一个模型作为Person模型和每个不同角色的中间人。这种中间模型实际上具有多态关联。您的人员模型将has_many该模型,您的各种角色模型将has_one。然后使用: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

答案 1 :(得分:0)

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

清洁简单。