Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you validate the presence of one field from many

I'm answering my own questions - just putting this up here for google-fu in case it helps someone else. This code allows you to validate the presence of one field in a list. See comments in code for usage. Just paste this into lib/custom_validations.rb and add require 'custom_validations' to your environment.rb

#good post on how to do stuff like this  http://www.marklunds.com/articles/one/312

module ActiveRecord
  module Validations
    module ClassMethods

      # Use to check for this, that or those was entered... example:
      #  :validates_presence_of_at_least_one_field :last_name, :company_name  - would require either last_name or company_name to be filled in
      #  also works with arrays
      #  :validates_presence_of_at_least_one_field :email, [:name, :address, :city, :state] - would require email or a mailing type address
      def validates_presence_of_at_least_one_field(*attr_names)
        msg = attr_names.collect {|a| a.is_a?(Array) ? " ( #{a.join(", ")} ) " : a.to_s}.join(", ") +
                    "can't all be blank.  At least one field (set) must be filled in."
        configuration = {
          :on => :save,
          :message => msg }
        configuration.update(attr_names.extract_options!)

        send(validation_method(configuration[:on]), configuration) do |record|
          found = false
          attr_names.each do |a|
            a = [a] unless a.is_a?(Array)
            found = true
            a.each do |attr|
              value = record.respond_to?(attr.to_s) ? record.send(attr.to_s) : record[attr.to_s]
              found = !value.blank?
            end
            break if found
          end
          record.errors.add_to_base(configuration[:message]) unless found
        end
      end

    end
  end
end
like image 290
Ben Wiseley Avatar asked Dec 02 '09 19:12

Ben Wiseley


1 Answers

This works for me in Rails 3, although I'm only validating whether one or the other field is present:

validates :last_name, :presence => {unless => Proc.new { |a| a.company_name.present? }, :message => "You must enter a last name, company name, or both"}

That will only validate presence of last_name if company name is blank. You only need the one because both will be blank in the error condition, so to have a validator on company_name as well is redundant. The only annoying thing is that it spits out the column name before the message, and I used the answer from this question regarding Humanized Attributes to get around it (just setting the last_name humanized attribute to ""

like image 131
swrobel Avatar answered Nov 12 '22 07:11

swrobel