始终使用关联的模型子类

问题描述 投票:-1回答:1

我有一种情况,我有基本模型,我想添加业务逻辑。例如,我可能会有这样的事情。

class List < ApplicationRecord
  has_many :subscriptions
  has_many :subscribers, though: :subscriptions
end

class Subscriber < ApplicationRecord
  has_many :subscriptions
  has_many :lists, through: :subscriptions
end

class Subscription < ApplicationRecord
  belongs_to :list
  belongs_to :subscriber
end

通过常规关联方法可以轻松订阅和取消订阅。

# Subscribe
list.subscriptions.create(
    subscriber: subscriber
)

# Unsubscribe
list.subscriptions.destroy(subscription)

# Unsub from all lists
subscriber.subscriptions.destroy_all

但是有日志记录和跟踪以及指标和挂钩以及其他业务逻辑。我可以用回调做到这一点。但是我想保持基本模型简单灵活。我的愿望是将核心功能与额外的业务逻辑分开。现在这是为了简化测试。最终,我需要在同一个核心之上添加两组不同的业务逻辑。

目前我正在使用服务对象来包装所有当前业务逻辑的常见操作。这是一个简单的例子,还有更多。

class SubscriptionManager
  def subscribe(list, subscriber)
    list.subscriptions.create( subscriber: subscriber )
    log_sub(subscription)
  end

  def unsubscribe(subscription)
    subscription.list.subscriptions.destroy(subscription)
    log_unsub_reason(subscription)
  end

  def unsubscribe_all(subscriber)
    subscriber.subscriptions.each do |subscription|
      unsubscribe(subscription)
    end
    subscriber.lists.reset
    subscriber.subscriptions.reset
  end
end

但我发现它越来越尴尬了。例如,我不能使用自然的subscriber.subscriptions.destroy_all,但必须小心通过SubscriptionManager方法。 Here's another example这个系统导致很难找到bug。

我正在考虑消除SubscriptionManager,而是编写具有额外逻辑的模型的子类。

class ManagedList < List
  has_many :subscriptions, class_name: "ManagedSubscription"
  has_many :subscribers, though: :subscriptions, class_name: "ManagedSubscriber"
end

class ManagedSubscriber < Subscriber
  has_many :subscriptions, class_name: "ManagedSubscription"
  has_many :lists, through: :subscriptions, class_Name: "ManagedList"
end

class ManagedSubscription < Subscription
  belongs_to :list, class_name: "ManagedList"
  belongs_to :subscriber, class_name: "ManagedSubscriber"

  after_create: :log_sub
  after_destroy: :log_unsub
end

问题是我发现我必须复制所有关联以保证托管对象与其他托管对象相关联。

有更好,更少冗余的方式吗?

ruby-on-rails model ruby-on-rails-5
1个回答
0
投票

我真的不明白为什么你需要在子类中再次定义关联。但是,我有一个提示,你可以直接在你的Subscription模型中使用。

如果您希望保持模型简单,并且不使用回调逻辑重载它,则可以创建一个callback class来包装模型将使用的所有逻辑。

为此,您需要创建一个类,例如:

class SubscriptionCallbacks

  def self.after_create(subscription)
    log_sub(subscription)
  end

  def self.after_destroy(subscription)
    log_unsub_reason(subscription)
  end

end

然后在Subscription模型:

class Subscription < ApplicationRecord
  belongs_to :list
  belongs_to :subscriber

  after_destroy SubscriptionCallbacks
  after_create SubscriptionCallbacks
end

这样,您的模型就会变得干净,您可以destroy订阅并应用所有自定义逻辑而无需使用服务。

UPDATE

具体来说,我不明白为什么你在三个模型上制作Single Table Inheritance只是为了给其中一个模型添加回调。您编写问题的方式,对于三个子类,您重写关联以使用创建的子类。这真的有必要吗?我认为不,因为你想要实现的只是重构你的服务作为回调,以便在destroy模型中直接使用destroy_allSubscription,我从这里开始:

但我发现它越来越尴尬了。例如,我无法使用自然的subscriber.subscriptions.destroy_all,但必须小心通过SubscriptionManager方法。

也许与conditional callbacks足够,甚至只是你的Subscription模型上的正常回调。

我不知道真正的代码是如何编写的,但我发现使用单表继承只是为了添加回调很棘手。这并不能使您的模型“简单而灵活”。

更新2

在回调类中,您使用要实现的回调的名称定义方法,并将subscription作为参数传递。在这些方法中,您可以创建所需的所有逻辑。例如(假设您将使用给定type属性的不同逻辑):

 class SubscriptionCallbacks

   def after_create(subscription)
     if subscription.type == 'foo'
       log_foo_sub(subscription)
     elsif subscription.type == 'bar'
       log_bar_sub(subscription)
     end
   end

   private

   def log_foo_sub(subscription)
     # Here will live all the logic of the callback for subscription of foo type
   end

   def log_bar_sub(subscription)
     # Here will live all the logic of the callback for subscription of bar type
   end

 end 

这可能是很多逻辑,不会在Subscription模型上写。您可以像往常一样使用destroydestroy_all,如果if else中没有定义订阅类型,那么什么都不会发生。

回调的所有逻辑都将包含在callback class中,并且您将添加到subscription模型的唯一代码安静将是:

 class Subscription < ApplicationRecord
   belongs_to :list
   belongs_to :subscriber

   after_create SubscriptionCallbacks.new
 end
© www.soinside.com 2019 - 2024. All rights reserved.