Rails:处理实体子类型的 STI 最佳方法



我有一个实体Order,它由两个子实体SalesOrderPurchaseOrder组成。这两个子实体共享其 95% 的行为。

单表继承和下面的代码是最好地解决这个问题的方法,还是有更好的方法?

class Order < ActiveRecord::Base
  belongs_to :order_type
end
class SalesOrder < Order       
  before_validation(on: :create) do
    self.order_type = OrderType.find_by!(name: "Sales order")
  end
  validates_inclusion_of :order_type_id, in: [OrderType.find_by!(name: 'Sales order').id]  
  validate :potential_savings, numericality: true, allow_nil: true
  default_scope { joins(:order_type).where("order_types.name = ?", "Sales order") }
  # OrderItem also has two sub entities (SalesOrderItem and PurchaseOrderItem with slightly different behaviour)
  has_many :order_items, class_name: "SalesOrderItem", foreign_key: "order_id", inverse_of: :order  
end
class PurchaseOrder < Order       
  before_validation(on: :create) do
    self.order_type = OrderType.find_by!(name: "Purchase order")
  end
  validates_inclusion_of :order_type_id, :in => [OrderType.find_by!(name: 'Purchase order').id]  
  validates_inclusion_of :potential_savings, :in => [nil], allow_nil: true # throw error if not nil
  default_scope { joins(:order_type).where("order_types.name = ?", "Purchase order") }
  # OrderItem also has two sub entities (SalesOrderItem and PurchaseOrderItem with slightly different behaviour)
  has_many :order_items, class_name: "PurchaseOrderItem", foreign_key: "order_id", inverse_of: :order
end

我不知道您的数据模型的所有细节,但也许您可以将共享功能放入Concern中。

像这样:

module Orderable
  extend ActiveSupport::Concern
  included do
    cattr_reader :order_type
    # All common/shared relations and validations are defined here
    belongs_to :order_type
    before_validation(on: :create) do
      self.order_type = OrderType.find_by!(name: @@order_type)
    end
    validates_inclusion_of :order_type_id, in: [OrderType.find_by!(name: @@order_type).id] 
    default_scope { joins(:order_type).where("order_types.name = ?", @@order_type) }
  end
  module ClassMethods
    def set_order_type(order_type)
      self.class_variable_set("@@order_type", order_type)
    end
  end
end
class SalesOrder < ActiveRecord::Base
  # Include the concern and call a method on the concern to set the order_type
  include Orderable
  set_order_type 'Sales order'
  # This validation is too different from the corresponding PurchaseOrder validation
  # so it is put in the model and not in the Orderable concern
  validate :potential_savings, numericality: true, allow_nil: true
end
class PurchaseOrder < ActiveRecord::Base
  include Orderable
  set_order_type 'Purchase order'
  # This validation is too different from the corresponding PurchaseOrder validation
  # so it is put in the model and not in the Orderable concern
  validates_inclusion_of :potential_savings, :in => [nil], allow_nil: true # throw error if not nil
end

不确定这是否更强大或干燥,然后采用 STI 方式。此外,由于order_type名称的格式不同,尚未实现最后一次验证,但可以修复。

我希望这会有所帮助或提供一些灵感。

销售订单就是订单。所以,我认为继承是最好的方法。如果两种类型具有相同或几乎相同的数据,则 STI 是最佳策略。如果您觉得这种方法会创建很多空列,我建议您尝试不同的策略,但保留继承。

最新更新