Reti
Reti

Reputation: 619

Ruby on Rails: Validating and changing a phone number

I have a simple phone form <%= f.text_field :phone %> right now. :phone is an integer type, so this requires that whatever the user enters into the form must be something like 5551234 instead of the more standard way of 555-1234 How can I allow the user to enter in a USA phone number like their accustomed to? I understand that I can use validates_format_of to validate it, but once I validate it, how can I format the number and insert the phone number into the DB as an integer?

Upvotes: 7

Views: 16171

Answers (4)

Niels
Niels

Reputation: 171

ActiveRecord automatically typecasts input based on the databases column type. When Ruby casts a string to an integer it drops everything after the first non-numeric character, 123-456-7890 will become 123. This is done before the field is available in the model so the solutions given so far will not work. You need to overwrite the default write accessor!

The ActiveRecord::Base docs mentions two ways to overwrite the default write accessor (field_name)= in the model so you can process the input removing the non-digits before it needs to be typecast. There are at least three variations:

(1) Overwrite the accessor and use write_attribute to store the result in the database:

def phone=(value)
  value.gsub!(/\D/, '') if value.is_a?(String)
  write_attribute(:phone, value.to_i)
end

(2) Or use the hash notation:

def phone=(num)
  num.gsub!(/\D/, '') if num.is_a?(String)
  self[:phone] = num.to_i
end

(3) Or (in the latest versions of ActiveRecord) just call super as if it were a normal (non-dynamic) method (not shown in docs but works):

def phone=(num)
  num.gsub!(/\D/, '') if num.is_a?(String)
  super(num)
end

This is useful in a number of situations such as numbers with commas and works great with forms that supply a formatted version of the previous field value such as in an edit form or after an error in a new/create form:

<%= f.text_field :phone, number_to_phone(@model_data.phone) %>

This way you can show the user formatted data (i.e. as String) but still store an integer in the database.

One last tip on MySQL: You need to use a BigInt to store a phone number otherwise you will see a lot of (214) 748-3647 phone numbers in your database as 2,147,483,647 is the max value of a normal MySQL integer - int(11) - obtained form :integer in a migration. A BigInt in MySQL is obtained by setting :limit to 8 as in this migration line:

change_column :model_name, :phone, :integer, :limit => 8

This will give you a bigint(20) in your table which can handle numbers up to 9,223,372,036,854,775,807 which should be sufficient for any phone number.

Upvotes: 17

Josiah
Josiah

Reputation: 114

Not sure if you got this figured out yet, but I ran into the same problem as Reti did with Mark's code.

It seems that specific self.phone = self.phone.gsub(/\D/, '') did the trick.

Maybe it's because at that point phone isn't initialized yet? I'm not sure, still a newbie here...

Upvotes: 0

Mark Thomas
Mark Thomas

Reputation: 37517

phone.gsub(/\D/, '')

should do the trick. It removes non-digit chars.

Upvotes: 12

Karl
Karl

Reputation: 6165

"1-2-3-4".gsub('-','').to_i
# => 1234

You can also use Regular Expressions if you want to get more fancy, but you should be able to get away with just this.

You probably want to validate what you're storing it in the DB as, since Rails checks validations before saving. However, you can do a before_validation that modifies the number first. Something like this:

before_validation do
  phone = phone.to_s.gsub('-','').to_i
end

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M001378

Upvotes: 0

Related Questions