Tái sử dụng validation khi sử dụng form object trong rails
Bài đăng này đã không được cập nhật trong 7 năm
Nếu bạn đang sử dụng pattern "Form Objects" và các validations không đặt ở ActiveRecord model, có thể bạn sẽ gặp khó khăn khi muốn sử dụng lại những validations tùy chỉnh (custom) ở những class khác nhau.
Tôi sẽ đưa cho các bạn ví dụ sau: Tưởng tượng bạn phải viết 1 đoạn mã validate cho một tiến trình thanh toán được cũng cấp bởi nhiều dịch vụ thanh toán khác nhau. Vì vậy bạn phải tách riêng từng class để xử lý cho mỗi dịch vụ thanh toán khác nhau.
class StripePurchase
include ActiveModel::Model
attr_accessor :token
validates :token, presence: true
def call
return false if invalid?
#Process Stripe payment
end
end
class PayPalPurchase
include ActiveModel::Model
attr_accessor :success_url, :failure_url
validates :success_url, :failure_url, presence: true
def call
return false if invalid?
#Process PayPal payment
end
end
User is purchasing the product, so we need to check if it exists. Khi mà người dùng muốn thanh toán cho một sản phẩm. Trước hết bạn phải kiểm tra xem sản phẩm đó có tồn tại hay không? Việc kiểm tra này sẽ thực hiện cho mọi hình thức thanh toán.
class ValidateProduct
include ActiveModel::Model
attr_accessor :product_id
validate :product_presence
validate :product_availability
private
def product_presence
errors.add(:product_id, :invalid) unless Product.where(id: product_id).exists?
end
def product_availability
errors.add(:product_id, :not_available) unless Stock.where(product_id: product_id).exists?
end
end
Gọi validation:
class StripePurchase
include ActiveModel::Model
attr_accessor :token, :product_id
validates :token, presence: true
def call
validate_product = ValidateProduct.new(product_id: product_id)
unless validate_product.valid?
define_singleton_method(:errors) { validate_product.errors }
return false
end
return false if invalid?
#Process Stripe payment
end
end
Trong khi thanh toán, người dùng có thể nhập voucher giảm giá, vì vậy chúng tao cần phải kiểm tra xem input đó có tồn tại và có được sử dụng với sản phẩm đang thanh toán hay không.
class ValidateVoucher
include ActiveModel::Model
attr_accessor :voucher, :product_id
validate :voucher_presence
private
def voucher_presence
errors.add(:voucher, :invalid) unless Voucher.where(code: voucher, product_id: product_id).exists?
end
end
Giờ đây nếu validation đầu tiên không thỏa mãn thì tiến trình sẽ dừng lại luôn mà không cần check các validation tiếp theo. Cũng có nghĩa là nếu không có sản phẩm thì cũng không có giảm giá nào cho nó cả.
class StripePurchase
include ActiveModel::Model
attr_accessor :token, :product_id, :voucher
validates :token, presence: true
def call
validate_product = ValidateProduct.new(product_id: product_id)
unless validate_product.valid?
define_singleton_method(:errors) { validate_product.errors }
return false
end
validate_voucher = ValidateVoucher.new(voucher: voucher, product_id: product_id)
unless validate_voucher.valid?
define_singleton_method(:errors) { validate_voucher.errors }
return false
end
return false if invalid?
#Process Stripe payment
end
end
Với hướng dẫn trên từ giờ bạn có thể tách các object thành các thành phần nhỏ riêng biệt và sử dụng trong những chỗ khác nhau
All rights reserved