0

我有一个 Order 对象,它是belongs_toaBillingAddress和 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

我有四个方法和三个注册的回调来满足这个需求。我也在破解错误消息。我在控制器中没有逻辑,形式相对简单。

= 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.

问题:

  1. 如何改进我的实施?
  2. 切换关系会有所帮助吗?-Order has_one :billing_addresshas_one :shipping_address不是belongs_to. 嵌套形式会感觉更自然;在那里,父母创造孩子,而不是相反。

我正在阅读相当多的重构书籍,但我永远无法将它们的示例映射到我自己的对象设计中。我猜我没那么有经验。我正在使用 Rails 4。

4

2 回答 2

1

如果他们检查了“帐单地址与送货地址相同”,您甚至不应该尝试验证或保存单独的帐单地址,因此它不应该有任何验证错误。

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

此外,您的“bill_to_shipping”绝对应该是数据库中的布尔值。如果您需要将参数转换为布尔值,请在保存记录时进行,而不是每次从数据库中获取它时进行。

于 2013-05-09T14:22:01.907 回答
1

起初我不认为维护一个属性bill_to_shipping_address是必要的。在这种情况下,您需要保留三个属性:“shipping_address”、“billing_address”和“billing_to_shipping_address”。那是多余的。

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

对于建模,我假设 Order 中会有 shipping_address_id 和 billing_address_id,它们都引用了 Address 中的某个地址 id。

在视图中,可以这样处理

  • 如果没有使用 JS。您提供另一组地址字段,并告诉用户仅在他们想要运送到与帐单地址不同的地址时才填写它。

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

  • 为了更好的用户体验,您可以添加一个#preview 方法,让用户最终确认所有输入。选修的。

在控制器#create 中,

  • 如果送货地址字段具有有效值,则表示用户想要不同的送货地址。无需比较,只需将值保存到 Address 中,并将 id 保存在 shipping_address_id 中。

  • 如果送货地址字段为空,那很好,只需将 billing_address_id 复制到 shipping_address_id 中即可。

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

于 2013-05-09T15:20:40.633 回答