Rails:使用 STI 为客户和合作伙伴记录建模

Rails: using STI to model client and partner records

我知道 STI 是 Rails 社区(可能还有其他人)中的一个有争议的话题,这就是我在走 STI 路线之前试图找到解决我的问题的不同方法的原因。

我正在构建一个具有联系人管理部分的系统,其中包含 clientpartner 记录。不同之处在于 partners 将有一个关联的 partner_type 和一些 client 没有的附加字段。

这看起来是 STI 的一个好案例。这些记录是相同的 "category",这意味着它们都代表 "people",但方式不同。他们都会有相同的核心领域,并且有很多 email_addresses/phone_numbers.

但导致我使用 STI 而不是单独表格的最大要求是我需要按字母顺序列出所有联系人。我的雇主不希望客户记录和合作伙伴记录的页面不同。如果我将其分成多个表,我将不得不以某种方式查询这两个表并按字母顺序排列它们,同时还要考虑分页(每种类型将有数千条记录)。

除了STI还有其他解决方案吗?我知道许多开发人员之前 运行 遇到过 STI 问题,但我倾向于这是一个教科书案例,其中 STI 可能确实有效。

class Contact < ApplicationRecord
  has_many :email_addresses # probably use polymorphic
  has_many :phone_numbers # probably use polymorphic 

  validates :first_name, :last_name, presence: true
end

class Client < Contact
end

class Partner < Contact
  belongs_to :partner_type

  validates :partner_type, presence: true

  # some attributes only applicable to client
  validates :client_unique_field1, :client_unique_field2, presence: true
end

您需要做出两个设计决定来实现此案例:

  1. partnersclients是否应该共享相同的table?

    一个。如果 "no",那么您只需创建单独的 table 和单独的模型。

    b。如果 "yes",那么您还有第二个设计问题要回答 #2。

  2. partnersclients是否应该共用同一个模型class?

    一个。如果 "yes",那么您可以使用 emum 来识别 partnerclient 的不同角色,并使用 enum 来驱动您的业务逻辑。

    b。如果 "no" 那么您应该实施 STI。

我认为对#1 说 "yes" 是有充分理由的。看来 clientpartner 从根本上讲是同一回事。他们都是人。 更重要的是,它们将包含大部分相同的信息,因此共享 table 很有意义。

因此,您将决定是使用 STI 还是 enum。您需要围绕与 partnersclients.

相关的业务逻辑做出基本决策

如果大部分业务逻辑是共享的,那么使用 enum 是有意义的。让我举一个例子。在我的一个项目中,我有一个 User 模型。所有用户都可以在网站上做基本的事情。但是,我们也有 school_admin 个用户和 class_admin 个用户。管理员当然对网站的某些部分有更多的访问权限,但从业务逻辑的角度来看,只有几个关系和几个方法是管理员独有的,用户不共享。

由于 95% 的业务逻辑由普通用户和管理员共享,因此我选择将它们全部集中在一起 class。我用了一个叫roleenum来区分用户:

# in the User model
enum :role, [:graduate, :school_admin, :class_admin]

users table 中,我有一个名为 roleint 类型的列。 enum开辟了一堆辅助方法,比如class_admin?,让业务逻辑正常工作。

您的情况可能有所不同。看来 clientspartners 在您的应用程序中的业务逻辑可能有更大的差异。我不知道,但听起来他们的角色存在一些根本差异。您将必须决定它们之间共享多少业务逻辑以及有多少不同。如果它们足够不同,那么 STI 就有意义。

此外,如果您想在方法中利用继承,您可能想要走 STI 路线。例如:您可能有一个 contact_verified? 方法,其中 partner.contact_verified?client.contact_verified?(仅电子邮件)具有不同的业务逻辑(电子邮件和 phone 可能)。也许是一个薄弱的例子,但你明白了。当然,在使用单一模型方法时,您可以在 contact_verified? 内部使用条件来完成同样的事情。

你说得对some in the Rails community tend to be down on STI。所以不要轻易决定走 STI 路线。但是,我在一些应用程序中成功地使用了 STI,几乎没有出现与 STI 相关的问题。

这完全取决于共享多少业务逻辑以及是否要利用继承。最终决定权在您。

Tom Aranda 提供了一个很好的框架来决定一种方法(看起来你应该使用一个 table)。但是,即使您决定使用两个 table,您的 "biggest" 要求也可以通过 UNION 查询在 SQL 中轻松解决。

SELECT * FROM (SELECT id, 'Client' as type, first_name, last_name FROM clients 
UNION SELECT id, 'Partner' as type, first_name, last_name FROM partners) AS t1
ORDER BY last_name LIMIT 25;

您可以更进一步 INNER JOIN 电子邮件地址和 phone 号码。