Reputation: 619
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
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
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
Reputation: 37517
phone.gsub(/\D/, '')
should do the trick. It removes non-digit chars.
Upvotes: 12
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