2013-05-09 44 views
0

我有一個訂單對象,belongs_to a BillingAddressShippingAddress。我想僅向我的用戶提供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

+1

我可能會改變驗證,所以它只驗證帳單地址,如果地址不相同,我會使用實際布爾值布爾值 - 這應該是透明地發生。在需要進一步清理之前,我會放棄這一點,但是我傾向於在主要清理工作之前停下來,直到他們證明有必要。 – 2013-05-09 13:16:33

+0

@DaveNewton我試圖使用布爾值,但複選框始終提交'0'和'1',這需要我添加一些邏輯來將其轉換爲布爾值。至於你的建議,我試圖做到這一點,但它被證明比我想象的更難。驗證在「地址」中定義,這意味着它們將始終啓動。我可以在我的'Order'模型中驗證這個關聯的存在,我已經在這樣做了。 – Mohamad 2013-05-09 13:19:22

+1

+ Mohamad,我記得在Rails中'1'to_boolean'或'1.to_boolean'是'true','0'.to_boolean'或'0.to_boolean'是'false'。 – DNNX 2013-05-09 14:58:38

回答

1

如果他們檢查「帳單地址相同收貨地址」,你甚至不應該嘗試驗證或保存一個獨立的帳單地址,因此它不應該有任何驗證錯誤。

由於表單提交正在創建多個模型,我建議單獨OrderBuilder服務對象(有可能是一個更好的名字),你從控制器調用。這樣你的訂單模型不需要如此關心清除地址錯誤。您的訂單構建器可以負責創建訂單和任何地址記錄,或者將寄送地址複製到帳單地址字段。

此外,您的'bill_to_shipping'應該肯定是數據庫中的布爾值。如果您需要將參數轉換爲布爾值,請在保存記錄時進行,而不是每次從數據庫中獲取記錄。

+0

感謝您的建議。但是,我不確定是否不保存帳單郵寄地址......那麼您如何處理帳單郵寄地址的檢查?您必須始終檢查帳單地址是否已在訂單中標記爲運輸,並相應地委託代理商? – Mohamad 2013-05-09 14:25:38

+0

在您的服務對象中,檢查帳單和運費是否應該相同。如果不是,則需要並驗證這兩個地址。如果它們相同,我可能會驗證一個地址,並且只有在它有效時纔將其複製到帳單地址。基本上,讓每個ActiveRecord :: Base子類擔心只有一個表,使用單獨的對象來協調多個記錄。 – sockmonk 2013-05-09 14:29:16

+0

我明白了,我試着首先使用服務對象,但我不確定如何確保對象與事務保存在一起。我是否必須在服務對象中使用事務塊,還是有辦法讓Rails自動處理該部分?我會需要這個,因爲我會放棄'accept_nested_attributes_for'。 – Mohamad 2013-05-09 14:31:30

1

起初我不認爲保留屬性bill_to_shipping_address是必要的。在這種情況下,您需要保留三個屬性:「shipping_address」,「billing_address」和「billing_to_shipping_address」。這是多餘的。

在我看來,送貨地址是送貨地址和帳單地址是帳單地址。您始終會收到送貨地址和賬單地址。

對於造型,我認爲會有一個shipping_address_id和billing_address_id才能,都是指某個地址的ID中的地址。

在視圖中,你可以處理像這樣

  • 如果沒有使用JS。您提供了另一組地址字段,並告訴用戶只有在發送到與帳單地址不同的地址時才填寫地址字段。

  • 如果使用JS,您可以選中一個複選框「運送到帳單地址」。一旦用戶取消選中,您將插入新的地址字段集。 注:複選框本身是沒有意義的作爲參數,並不會在控制器

  • 被認爲是爲了更好的用戶體驗,您可以添加這讓用戶最終確認所有輸入#preview方法。可選的。

在控制器#創建,

  • 如果送貨地址字段具有有效的值,則意味着用戶需要不同的送貨地址。無需進行比較,只需將值保存到地址和shipping_address_id中的ID即可。

  • 如果送貨地址字段爲空,那很好,只需將billing_address_id複製到shipping_address_id。

希望上面的內容能給你一些啓發。