重构ActiveRecord自定义验证

时间:2013-09-10 13:24:39

标签: ruby-on-rails validation refactoring ruby-on-rails-4 rails-activerecord

我有一个名为PaymentNotifications的模型。它仅用于记录付款,只要它们从Paypal有效。我需要检查他们给我的交易代码是否与我发布表单后从他们那里得到的交易代码相同。

所有这些都有效。我所做的是根据以下某些标准检查它是否有效:

在控制器中我有以下内容:

tx = params[:tx] 
paypal_data = get_data_from_paypal(tx)
res_hash = create_hash(paypal_data) 
@payment_notification = PaymentNotification.new(:params => res_hash, :quotation_id => res_hash['invoice'],:status => res_hash["payment_status"],:transaction_id => res_hash["txn_id"])
if paypal_data["SUCCESS"] && @payment_notification.is_valid?(tx) && @payment_notification.save
  redirect_to thankyou_path(:id => @payment_notification.quotation_id)
else
  render '/pages/error'
end

然后在模型中我运行我的方法is_valid?

validates :params, :quotation_id, :status, :transaction_id, presence: true
validates :transaction_id, :uniqueness => true

def is_valid?(tx)
  amount_paid_valid?(params["payment_gross"]) && transaction_valid?(tx) && is_quotation_unpaid? 
end

def transaction_valid?(tx)
  if tx != transaction_id
    errors.add(:transaction_id, "This transaction is not valid")
    return false
  else
    return true
  end
end

def is_quotation_unpaid?
  if quotation.unpaid?
    return true
  else
    errors.add(:quotation_paid, "This quotation has already been paid.")
    return false
  end
end

def amount_paid_valid?(amount_paid)
  if amount_paid.to_i == quotation.price.to_i
    return true
  else
    errors.add(:amount_paid, "The amount paid does not match the price quoted.")
    return false
  end
end

注意::amount_paid:quotation_paid不是属性。它们只是错误消息的关键。

我想我在这里错过了这条船,因为必须有一种方法可以使用Rails中内置的验证来实现这一点,但我还不太擅长Rails。有人可以帮我重构一下,以便更容易维护并符合最佳实践吗?

1 个答案:

答案 0 :(得分:1)

这里的主要问题是你重新实现了Rails已经拥有的东西 - 即一种检查AR对象是否有效的方法。如果您使用的是方法而不是内置的#valid?,那么您的对象会继续传递#save#create等操作,即使它们不应该这样做。

为了将您的自定义方法放入折叠并在调用内置验证时包含它们,只需在模型中将它们用作自定义验证,如下所示:

validates :params, :quotation_id, :status, :transaction_id, presence: true
validates :transaction_id, :uniqueness => true
validate :amount_paid_should_match_quote, :quotation_should_be_unpaid
validates_associated :transaction

private

def amount_paid_should_match_quote
  if amount.to_i != quotation.price.to_i
    errors.add(:amount, "does not match the price quoted")
  end
end

def quotation_should_be_unpaid
  if quotation.paid?
    errors.add(:quotation, "has already been paid")
  end
end

要注意的几个项目:

  1. 验证方法不应该使用参数,因为它们是测试现有属性的实例方法。
  2. 避免在模型中引用参数。处理请求是控制器的工作。
  3. 您只需要处理方法中的非传递方案。当对象有效时,不要担心返回true,这取决于Rails。
  4. 不要编写验证关联的方法。只需使用validates_associated即可。
  5. 如果您重命名自定义方法,以便更好地描述他们尝试执行的实际行为,则会有所帮助。我试图给你一个建议,但你可以使用你喜欢的任何东西。
  6. 您可以在Rails Guides Validations documentation了解有关自定义验证的详情。