Ruby on rails 如何重构此对象以减少对回调的依赖?

Ruby on rails 如何重构此对象以减少对回调的依赖?,ruby-on-rails,ruby,refactoring,Ruby On Rails,Ruby,Refactoring,我有一个订单对象,属于aBillingAddress和aShippingAddress。我只想给我的用户提供ShippingAddress字段和一个复选框,指示账单地址与发货地址匹配。如果用户取消选中该框,则会显示BillingAddress字段 我的实现感觉很笨拙,我的Order对象有很多回调 class Order < ActiveRecord::Base attr_accessor :bill_to_shipping_address belongs_to :billing_

我有一个订单对象,
属于
a
BillingAddress
和a
ShippingAddress
。我只想给我的用户提供ShippingAddress字段和一个复选框,指示账单地址与发货地址匹配。如果用户取消选中该框,则会显示BillingAddress字段

我的实现感觉很笨拙,我的Order对象有很多回调

class Order < ActiveRecord::Base
  attr_accessor :bill_to_shipping_address

  belongs_to :billing_address,  class_name: 'Address'
  belongs_to :shipping_address, class_name: 'Address'

  accepts_nested_attributes_for :billing_address, :shipping_address

  after_initialize  :set_billing_to_shipping_address
  before_validation :set_billing_address
  after_validation  :clear_billing_address_errors

  # Init the object with option checked
  def set_billing_to_shipping_address
    self.bill_to_shipping_address ||= '1'
  end

  # Copy shipping address attrs to billing address
  def set_billing_address
    self.billing_address = self.shipping_address if bill_to_shipping_address?
  end

  def bill_to_shipping_address?
    bill_to_shipping_address == '1'
  end

  # If shipping address matches billing, we copy the attrs, and thus duplicate errors too.
  # We only need to show the user one set of errors if addresses are the same, so remove them for billing address.
  def clear_billing_address_errors
    if bill_to_shipping_address?
      self.errors.messages.each { |k,v| self.errors.messages.delete(k) if k.to_s.split('.')[0] == 'billing_address' }
    end
  end
end
问题:

  • 如何改进我的实现
  • 切换关系会有帮助吗?--
    订单有一个:账单地址
    有一个:发货地址
    ,而不是
    所属的地址
    。嵌套的形式会感觉更自然;在这种情况下,父母创造孩子,而不是相反
    我读了不少重构方面的书,但我永远无法将它们的示例映射到我自己的对象设计中。我想我没那么有经验。我使用的是Rails 4。

    如果他们选中“账单地址与发货地址相同”,您甚至不应该尝试验证或保存单独的账单地址,因此它不应该有任何验证错误

    由于表单提交正在创建多个模型,因此我建议您从控制器调用一个单独的OrderBuilder服务对象(可能有更好的名称)。这样,您的订单模型就不需要如此关心清除地址错误。您的订单生成器可以负责创建订单和任何地址记录,或将发货地址复制到账单地址字段


    此外,您的“账单到发货”在数据库中肯定应该是布尔值。如果需要将参数转换为布尔值,请在保存记录时进行转换,而不是每次从数据库中获取记录时进行转换。

    首先,我认为没有必要维护属性
    bill\u to\u shipping\u address
    。在这种情况下,您需要保留三个属性:“发货地址”、“账单地址”和“账单地址到发货地址”。那是多余的

    在我看来,发货地址是发货地址,账单地址是账单地址。您总是发送到发货地址,账单发送到账单地址

    对于建模,我假设将有一个发货地址id和账单地址id,它们都是指地址中的某个地址id

    在视图中,您可以这样处理它

    • 如果没有使用JS。您提供了另一组地址字段,并告诉用户仅当他们希望发送到与账单地址不同的地址时才填写该字段

    • 如果使用JS,您可以有一个已选中的复选框“Shipping to billing address”。一旦用户取消选中,您将插入新的地址字段集。注意:复选框本身作为参数没有意义,在控制器中不会考虑

    • 为了获得更好的用户体验,您可以添加#预览方法,让用户最终确认所有输入。可选

    在控制器#创建中

    • 如果配送地址字段具有有效值,则表示用户需要不同的配送地址。无需进行比较,只需将值保存到Address和shipping\u Address\u id中的id即可

    • 如果配送地址字段为空,则可以将账单地址id复制到配送地址id中


    希望上面的内容能给你一些启发。

    我可能会更改验证,使其仅在地址不相同时验证账单地址,并且我会使用实际的布尔值作为布尔值——这应该是透明的。在需要进一步清理之前,我不会再这样做,但我倾向于推迟主要清理,直到证明有必要为止。@DaveNewton我尝试使用布尔值,但复选框总是提交“0”和“1”,这要求我添加一些逻辑以将其转换为布尔值。至于你的建议,我试着这么做,但事实证明比我想象的要难。验证在
    地址
    中定义,这意味着它们将始终启动。我可以在我的
    顺序
    模型中验证关联的存在,我已经在这么做了。+Mohamad,我记得,
    '1'。to _boolean
    1。to _boolean
    在Rails中是
    真的
    ,to _boolean
    0。to _boolean
    假的
    。感谢您的建议。但是,我不确定是否不保存帐单地址。。。那么,您将如何处理检查帐单地址的问题?您必须始终检查账单地址是否在订单中标记为配送,以及相应的委托人?在您的服务对象中,检查账单和配送是否应该相同。如果不是,则需要并验证这两个地址。如果它们是相同的,我可能会验证一个地址,并且只有在该地址有效的情况下才将其复制到账单地址。基本上,让每个ActiveRecord::Base子类只关心一个表,使用单独的对象来协调多个记录。明白了,我尝试先使用一个服务对象,但我不确定如何确保对象与事务一起保存。我是否必须在服务对象中使用事务块,或者是否有办法让Rails自动处理该部分?我需要这个,因为我将为
    删除
    接受\u嵌套的\u属性。\u您将需要那里的事务块,当然还有rescue ActiveRecord::RecordInvalid等等。我已经成功地为服务对象提供了errors属性,控制器可以引用该属性将错误返回给客户端,特别是在将json返回到ajax调用时。所以控制器仍然有标准的“if service_object.save然后redirect else render errors”模式。澄清一下,您不认为它超出了服务对象的范围吗?如果我删除
    接受\u嵌套的\u属性
    ,我必须删除
    字段。这意味着我不能提交表单数据
    
    = form_for @order do |f|
        # ...
        = f.label :bill_to_shipping_address, class: 'checkbox' do
          #{f.check_box :bill_to_shipping_address} Use my shipping address as my billing address.