Customize Validation với Validation Context

Đã bao giờ bạn tự hỏi làm thế nào để bỏ qua một hoặc nhiều validation trong một số trường hợp nhất định? Và hôm nay, tôi sẽ cùng các bạn tìm cách để giải quyết vấn đề này nhé. Điều đầu tiên nảy ra trong đầu tôi khi gặp vấn đề này là gán điều kiện cho validation. và chúng ta hãy thử xem.

tôi có 1 model là User

class User < ActiveRecord::Base
  validates_length_of :slug, minimum: 3
end

và bây giờ tôi chỉ muốn thực hiện validate này với user thường chứ không phải admin. tôi sẽ thực hiện như sau:

class User < ActiveRecord::Base
  attr_accessor: :edited_by_admin
  validates_length_of :slug, minimum: 3, unless: Proc.new{|u| u.edited_by_admin? }
end

và ở trên controller tôi sẽ gán edited_by_admin = true

class Admin::UsersController
  def edit
    @user = User.find(params[:id])
    @user.edited_by_admin = true
    if @user.save
      redirect # ...
    else
      render # ...
    end
  end
end

và bây giờ đoạn code này đã có thể đáp ứng nhu cầu của tôi, nhưng nó vẫn còn dài dòng và khó hiểu. Nên tôi sẽ giới thiệu đến các bạn Validation Context: vẫn với yêu cầu trên nhưng tôi chỉ cần thực hiện như sau

class User < ActiveRecord::Base
  validates_length_of :slug, minimum: 3, on: :user
end
class User < ActiveRecord::Base
 class Admin::UsersController
  def edit
    @user = User.find(params[:id])
    if @user.save(context: :admin)
      redirect # ...
    else
      render # ...
    end
  end
end

rất đơn giản phải không nào, và nếu bạn chỉ muốn kiểm check validation mà không lưu lại:

u = User.new
u.valid?(:admin)
# or
u.valid?(:user)

Chú ý: Nếu bạn muốn có một số validations thực trên:: admin và một số trên::create thì code sẽ làm việc theo cách bạn muốn. Ví dụ:

class User < ActiveRecord::Base
  validates_length_of :slug, minimum: 3, on: :user
  validates_length_of :slug, minimum: 1, on: :admin
  validate :something, on: :create
end

Khi bạn chạy user.valid?(:admin) hay user.save(context: admin) cho bản ghi mới, thì hệ thống sẽ kích hoạt validation cuối cùng. Và có một mẹo nhỏ nếu bạn muốn valid cả context mặc định(như: create, update, destroy) và custome context như :admin như sau:

class Admin::UsersController
  def edit
    User.transaction do
      @user = User.find(params[:id])
      if @user.valid?(:admin) && @user.valid?(:create)
        @user.save!(validate: false)
        redirect # ...
      else
        render # ...
      end
    end
  end
end

Trên đây là những giới thiệu ngắn gọn về Validation Context với Ruby on Rails, cảm ơn các bạn đã dành thời gian theo dõi.