Rails custom validation

1. Lảm nhảm Rails được biết đến với triết lý là COC (Convention over Configuration) không phải COCC nha =)) Coding trong Rails đều tuân theo nguyên lý này, cũng nhờ đó mà develop với Rails có thể đạt được hiệu qủa tương tự các framwork khác trong khi sử dụng ít code hơn, mà

1. Lảm nhảm

  • Rails được biết đến với triết lý là COC (Convention over Configuration) không phải COCC nha =))
  • Coding trong Rails đều tuân theo nguyên lý này, cũng nhờ đó mà develop với Rails có thể đạt được hiệu qủa tương tự các framwork khác trong khi sử dụng ít code hơn, mà less code thì less bug 😄😄😄
  • Tuy vậy, Rails cũng cung cấp khả năng override và custom mạng mẽ cho các built-in feature của framework, cho phép developer config lại các chức năng này cho phù hợp với dự án mình đang làm.
  • Dự án mình đang làm custom validation hơi nhiều nên mình sẽ sử dụng validation để minh hoạ cho khả năng này của Rails =))
  • Nội dung cũ nhưng hi vọng sẽ có ích cho ai đó 😄😄😄

2. Intrudution

a. Validation

  • Rails cung cấp các built-in validation để validate ở model trước khi insert / update dữ liệu vào DB.
  • Chúng ta khai báo 1 validation ở model với method validate.
  • Ví dụ với validates confirmation.
    validates :email, confirmation:true
    validates :email, confirmation:{ case_sensitive:true}
    validates :email, confirmation:{ case_sensitive:true}, message:"doesn't match"
    validates :email, confirmation:{ case_sensitive:true}, message:"doesn't match %{attribute}"
  • Với các ví dụ trên, bạn có thể thấy mình đang custom nhiều hơn qua nhiều ví dụ
  • Với ví dụ thứ nhất, mình chỉ đơn giản là khai báo validate confirmation với attribute, các option khác (case_sensitive, message, …) sẽ sử dụng config mặc định của Rails
    validates :email, confirmation:true
  • Với ví dụ thứ 2, mình đang custom thêm option mặc định về case_sensitive
    validates :email, confirmation:{ case_sensitive:true}
  • Với ví dụ thứ 3, mình lại custom thêm về message khi xảy ra lỗi
    validates :email, confirmation:{ case_sensitive:true}, message:"doesn't match"
  • Với ví dụ thứ 4, mình đang pass thêm params vào message lỗi
    validates :email, confirmation: { case_sensitive: true }, message: "doesn't match %{attribute}"
    
  • Các bạn có thể xem message của validate nào nhận params nào ở repo rails-i18n.
  • Ngoài việc custom lại message mặc định, Rails còn cho phép bạn custom lại message cho từng model hoặc attribute của model nữa cơ, thông qua việc sử dụng i18n
    en:activerecord:errors:models:user:confirmation:"user's %{attribute} doesn't match"attributes:password:confirmation:"user's password doesn't match"admin:attributes:password:confirmation:"admin's %{attribute} doesn't match"

b. Custom validates

  • Chúng ta đã giới thiệu sơ về khả năng custom validation của Rails, nhưng đó chưa phải là tất cả.
  • Bên cạnh các built-in validation, Rails còn cung cấp cho chúng ta khả năng custom thêm các validation mới.
  • Chúng ta sẽ tìm hiểu phần này thông qua ví dụ về validate format của email
    validates :email, format:{ with:/A([^@s]+)@(sun-asterisk.com)z/}
  • Validate email phải thuộc domain sun-asterisk.com.com

i. validate method

  • Cách thứ nhất để custom validates là cung cấp 1 method cho model để validate
    validate :email_sun_asterisk_formatprivatedefemail_sun_asterisk_formatreturnif email.to_s.match?(/A([^@s]+)@(sun-asterisk.com)z/)
    
      errors.add :email,:invalidend
  • Bạn có thể tìm hiểu thêm ở guides.rubyonrails.org
  • Cách này khá đơn giản tuy nhiên nó sẽ không DRY nếu validate logic được sử dụng ở nhiều model.
  • Bạn có thể tách thành concern và include vào các model cần thiết để DRY hơn

ii. validates_each

  • Cách thứ hai để custom validates là cung cấp 1 validator class cho model để validate
  • Tạo custom validator trong thư mục app/validators
    #app/validators/sun_asterisk_format_validator.rbclassSunAsteriskFormatValidator<ActiveModel::EachValidatordefvalidate_each(record, attribute, value)returnif value && value.match?(/A([^@s]+)@(sun-asterisk.com)z/)
    
        record.errors.add attribute,:invalidendend
  • Thêm thư mục app/validators vào load path
    # config/application.rb
    config.autoload_paths +=%W["#{config.root}/app/validators/"]
  • Sử dụng custom validator như 1 built-in validate bình thường khác
    # app/models/user.rb
    validates :email, presence:true
    validates :email, sun_asterisk_format:true
  • Bản thân mình thích cách thứ 2 hơn so với cách thứ nhất vì nó cung cấp syntax tương tự như built-in validators của Rails, code cũng đẹp và DRY hơn hơn 😄

c. More about validates_each

i. options

  • Trong ví dụ trên bạn có thể thấy hàm validate_each nhận 3 params là record, attribute, value

  • Tuy nhiên class ActiveModel::EachValidator còn cung cấp cho bạn 1 params nữa là options

  • Ví dụ

    #app/validators/sun_asterisk_format_validator.rbclassSunAsteriskFormatValidator<ActiveModel::EachValidatordefvalidate_each(record, attribute, value)returnif value && value.match?(/A([^@s]+)@(sun-asterisk.com)z/)
    
        record.errors.add attribute,(options[:message]||:invalid)endend
    # app/models/user.rb
    validates :email, sun_asterisk_format:{ message:"must be in sun-asterisk.com domain"}
  • Không chỉ message, bạn có thể sử dụng thêm các option khác cho custom validator của mình, custom thoải mái =))

iii. error message

  • Để add error mesage vào record ta có 2 cách
  • Cách 1
    record.errors[attribute]<<"string"
  • Với cách này, kể cả khi bạn << :key thì Rails sẽ convert :key sang stringvà sử dụng làm error message
    record.errors[attribute]<<:invalid# :invalid sẽ được convert thành "invalid"
    record.errors[attribute]<<I18n.t("errors.messages.invalid")# sử dụng error message mặc định cho mọi model
  • Cách này có khá nhiều khuyết điểm nên mình thường sử dụng cách 2
  • Cách 2
    record.errors.add attribute,:invalid
  • Với cách này bạn thậm chí còn có thể sừ dụng i18n riêng cho custom validator của mình thay vì sử dụng chung i18n với các built-in validators của Rails
    record.errors.add attribute,:invalid_sun_asterisk_format
    # config/locals/en.ymlen:hello:"Hello world"errors:messages:invalid_sun_asterisk_format:"must be in sun-asterisk.com domain"
  • Hoặc thêm params vào i18n của error message
    record.errors.add attribute,:invalid_sun_asterisk_format, value, value
    
    # config/locals/en.ymlen:hello:"Hello world"errors:messages:invalid_sun_asterisk_format:"must be in sun-asterisk.com domain, received %{value}"

3. Say bye

  • Link source code tham khảo minh đang để trên Github,
  • Hi vọng các bạn thích bài viết này
  • Ẹnjoy and happy coding 😄😄😄

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ