Tái sử dụng validation khi sử dụng form object trong rails
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 ...
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