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

现在,我使用 Ryan Bates 多页表单截屏视频 ( http://railscasts.com/episodes/217-multistep-forms ) 中的解决方案覆盖了 Devise 的 RegistrationsController 来处理多页表单。我不得不对其进行一些调整以使其与 Devise 一起使用,但我成功了。现在,因为 Ryan 的多页表单只是要求在不同页面上来自同一模型的不同字段,他能够valid?通过在他的 validate 方法中添加一个 :if 块来覆盖他的方法,例如:

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

但就我而言,我从我的父模型(用户)询问第一个表单上的所有字段,然后从我的两个孙模型(用户:PaymentProfile:Address,用户: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我在有效代码之后立即在代码中添加了一条消息?命令,它显示如下:

{: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"]]]}

现在这个输出的结构与标准错误输出的输出不匹配,标准错误输出是基于单层哈希构建的,例如:

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

因此,在向您展示了所有这些之后,我的问题是:a) 如何让错误输出正确显示,以便表单实际将它们吐出?b)我如何防止parent.valid?从也验证grandchildren.valid?当我不在那个页面上时?我不能在子模型上使用 :if => lambda... 解决方案,因为他们不知道 current_step 是什么。

我为这么长的帖子道歉,我只是想包含尽可能多的信息。我已经为此苦苦挣扎了一个星期,但我似乎无法克服它。任何建议都会非常有帮助。提前致谢。

4

3 回答 3

4

错误未显示的原因可能是它们是在基础上填充的,而不是在单个属性上。这发生在你的validate_cardvalidate_address方法中。而不是将错误添加到基础,您应该将它们添加到导致错误的特定属性。

errors.add( attr , error )

其次,如果您想让您的验证依赖于某个状态,如您提到的截屏视频,那么您需要将状态标志与模型一起保存(最好是父模型)。您可以手动执行此操作,或者更好的是,您可以为此使用 gem(推荐):state_machine

祝你好运。

于 2012-01-25T01:08:34.980 回答
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)

等等...

User 模型有一个 UserInformation,有一个 PaymentInformation 等等。

本质上用组合代替继承。尝试看看是否也可以避免扩展 ActiveMerchant 框架。

上面的样式,会给你更多的控制权,当你想调用#valid?在你的数据模型的一个子集上。当用户在表单中移动时,它会逐步构建。

抱歉,我没有针对您的具体解决方案,而是更通用的重写方法。

于 2012-01-24T05:58:10.607 回答
0

我是 Ruby-on-Rails 的新手,我知道这不能回答上述问题,但您应该尝试Client-Side Validations并查看Rails-casts。可能对你有帮助!

于 2011-07-08T12:49:30.593 回答