2011-03-28 79 views
6

我正在嘗試使用設計和活動商家編寫註冊。形式是複雜的,我的用戶對象是這樣的:在複雜模型上驗證多頁表單

class User < ActiveRecord::Base 
    include ActiveMerchant::Utils 

    # Include default devise modules. Others available are: 
    # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable 
    devise :database_authenticatable, :registerable, 
     :recoverable, :rememberable, :trackable, :validatable, :omniauthable 

    # Setup accessible (or protected) attributes 
    attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :first_name, 
        :subscription_attributes, :last_name, :zipcode, 
        :payment_profile_attributes, :customer_cim_id, :payment_profile_id 

... 

    # Track multi-page registration 
    attr_writer :current_step 

... 

    # Setup Payment Profile element (authorize.net billing profile) 
    has_one :payment_profile, :dependent => :delete 
    accepts_nested_attributes_for :payment_profile 

現在PaymentProfile類有自己的孩子,從活動的商人兩個項目:

require 'active_merchant' 

class PaymentProfile < ActiveRecord::Base 
    include ActiveMerchant::Billing 
    include ActiveMerchant::Utils 

    validate_on_create :validate_card, :validate_address 

    attr_accessor :credit_card, :address 

    belongs_to :user 

    validates_presence_of :address, :credit_card 

    def validate_address 
    unless address.valid? 
     address.errors.each do |error| 
     errors.add(:base, error) 
     end 
    end 
    end 

    def address 
    @address ||= ActiveMerchant::Billing::Address.new(
     :name  => last_name, 
     :address1 => address1, 
     :city  => city, 
     :state => state, 
     :zip  => zipcode, 
     :country => country, 
     :phone => phone 
    ) 
    end 

    def validate_card 
    unless credit_card.valid? 
     credit_card.errors.full_messages.each do |message| 
     errors.add(:base, message) 
     end 
    end 
    end 

    def credit_card 
    @credit_card ||= ActiveMerchant::Billing::CreditCard.new(
     :type    => card_type, 
     :number    => card_number, 
     :verification_value => verification_code, 
     :first_name   => first_name, 
     :last_name   => last_name 
    ) 
    @credit_card.month ||= card_expire_on.month unless card_expire_on.nil? 
    @credit_card.year ||= card_expire_on.year unless card_expire_on.nil? 
    return @credit_card 
    end 

現在我overrided的RegistrationsController從設計使用Ryan Bates多頁表格截屏視頻(http://railscasts.com/episodes/217-multistep-forms)的解決方案處理多頁表單。爲了與Devise合作,我不得不調整一下,但我很成功。現在,由於瑞安的多頁的形式跟它從不同的頁面上同一型號不同的領域,他能夠通過將覆蓋他的valid?方法:如果塊給他的validate方法一拉:

validates_presence_of :username, :if => lambda { |o| o.current_step == "account" } 

但在我的情況,我要求從我的父模型(用戶)的第一個窗體上的所有字段,然後要求從我的兩個孫模型(用戶:PaymentProfile:地址,用戶:PaymentProfile:Credit_Card)的所有字段第二頁。

我面臨的問題是,雖然PaymentProfile.valid?基於ActiveMerchant的邏輯返回錯誤,表單本身不渲染或甚至顯示這些錯誤。在結算頁面視圖代碼看起來是這樣的:

<h2>Payment Details</h2> 

<%= semantic_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> 
    <%= devise_error_messages! %> 

    <%= f.semantic_fields_for :payment_profile do |p| %> 
     <%= p.semantic_fields_for :address do |a| %> 
      <%= a.inputs "Billing Information", :id => "billing" do %> 
       <%= a.input :type, :label => "Credit Card", :as => :select, :collection => get_creditcards %> 
       <%= a.input :number,  :label => "Card Number", :as => :numeric %> 
       <%= a.input :card_expire_on, :as => :date, :discard_day => true, :start_year => Date.today.year, :end_year => (Date.today.year+10), :add_month_numbers => true %> 
       <%= a.input :first_name %>  
       <%= a.input :last_name %> 
       <%= a.input :verification_code, :label => "CVV Code" %> 
      <% end %> 
     <% end %> 

     <%= f.semantic_fields_for :credit_card do |c| %> 
      <%= c.inputs "Billing Address", :id => "address" do %> 
       <%= c.input :address1, :label => "Address" %> 
       <%= c.input :city %> 
       <%= c.input :state, :as => :select, :collection => Carmen::states %> 
       <%= c.input :country, :as => :select, :collection => Carmen::countries, :selected => 'US' %> 
       <%= c.input :zipcode, :label => "Postal Code" %> 
       <%= c.input :phone, :as => :phone %> 
      <% end %> 
     <% end %> 
    <% end %> 

    <%= f.commit_button :label => "Continue" %> 
    <% unless @user.first_step? %> 
    <%= f.commit_button :label => "Back", :button_html => { :name => "back_button" } %> 
    <% end %> 
<% end %> 

我的代碼中加入了puts errors消息後立即有效?命令,並將其顯示如下:

{:username=>["can't be blank"]} 

所以:

{:base=>[["first_name", ["cannot be empty"]], ["last_name", ["cannot be empty"]], ["year", ["expired", "is not a valid year"]], ["type", ["is required", "is invalid"]], ["number", ["is not a valid credit card number"]], ["verification_value", ["is required"]], ["address1", ["is required"]], ["city", ["is required"]], ["state", ["is required"]], ["zip", ["is required", "must be a five digit number"]], ["phone", ["is required", "must be in the format of 333-333-3333"]]]} 
{:base=>[["first_name", ["cannot be empty"]], ["last_name", ["cannot be empty"]], ["year", ["expired", "is not a valid year"]], ["type", ["is required", "is invalid"]], ["number", ["is not a valid credit card number"]], ["verification_value", ["is required"]], ["address1", ["is required"]], ["city", ["is required"]], ["state", ["is required"]], ["zip", ["is required", "must be a five digit number"]], ["phone", ["is required", "must be in the format of 333-333-3333"]]]} 

現在該輸出的結構,其是建立關單層散列諸如標準錯誤輸出的輸出不匹配在向您展示了所有這些信息之後,我的問題如下: a)如何正確顯示錯誤輸出以使表單實際吐出來? b)如何防止parent.valid?從也驗證孫子。有效?當我不在該頁面上?我不能在子模型上使用:if => lambda ...解決方案,因爲他們不知道current_step是什麼。

我對這麼長的帖子表示歉意,我只是想盡可能地包含更多的信息。我已經摔跤了一週,我似乎無法超越它。任何建議都會非常有幫助。提前致謝。

回答

4

錯誤未顯示的原因可能是它們被填充在基礎上,而不是單個屬性上。這發生在您的validate_cardvalidate_address方法中。您應該將其添加到導致錯誤的特定屬性中,而不是將錯誤添加到基礎中。

errors.add(attr , error) 

其次,如果你想使你的驗證依賴於某種狀態,正如你所說的截屏,那麼你需要保存狀態標誌與模型(可能是最好的父母)。你可以手工做到這一點,或者更好的是,你可以使用寶石(推薦):state_machine

祝你好運。

0

我是Ruby on Rails的新手,我知道這並不回答上面的問題,但您應該試試Client-Side Validations並查看Rails-casts。這可能對您有所幫助!

+2

客戶端驗證是一個很好的額外,但絕對沒有替代服務器端驗證。不使用啓用了JS的瀏覽器的惡意腳本很容易削弱客戶端驗證。爲確保您的數據具有高度的完整性,服務器端驗證是唯一可靠的機制。 – 2012-01-25 01:00:52

1

在高層次上,您似乎在對象建模中使用繼承,並且這種模型以幾乎「嚮導」的方式構建成多種形式。我的建議是模型的對象,以反映,實際的形式一樣,

First part of the form collect basic User information : UserInformation model 

Second Part of the form collect payment related information: PaymentInformation model (the Active merchant stuff) 

等等...

無論在哪裏的用戶模型有一個UserInformation,有一個PaymentInformation等。

本質上用組合替換繼承。試着看看你是否可以避免擴展ActiveMerchant框架的工作。

上面的樣式,會給你更多的控制權,當你想打電話#有效嗎?在你的數據模型的一個子集上。當用戶在表單中移動時,它會逐個構建。

對不起,我沒有針對你的具體解決方案,而是一個更一般的重寫方法。