Lets say, you wanted to store contact information such as emails, phone numbers, and websites. These contact information can be associated with an employee of a business or the business itself. A business can have multiple emails and multiple phone numbers. Similarly, an employee can have multiple emails and multiple phone numbers. In Rails, we can store all these contact information in a single table using both the polymorphic association and single-table inheritance. First, lets see how we can use polymorphic association to deal with the contact information.
We have the following active record models for a “Business” and an “Employee”:
class Business < ActiveRecord::Base; end
class Employee < ActiveRecord::Base; end
For the contact information, we have the following active record models:
class Email < ActiveRecord::Base; end
class PhoneNumber < ActiveRecord::Base; end
class Website < ActiveRecord::Base; end
Now, for the associations, Business and Employee models can have many emails, phone numbers and websites. And conversely, Email/Phone Number/Website can belong to either a Business model or an Employee model. How do we make up the association?
Rails ActiveRecord provides an association called “Polymorphic” association. Using polymorphic association, a model can belong to more than one other model, on a single association. Here is how this can be implemented:
class Business < ActiveRecord::Base
has_many :emails, :as => :emailable
has_many :phone_numbers, :as => phonable
has_many :websites, :as => webable
end
class Employee < ActiveRecord::Base
has_many :emails, :as => :emailable
has_many :phone_numbers, :as => phonable
has_many :websites, :as => webable
end
class Email < ActiveRecord::Base
belongs_to :emailable, :polymorphic => true
end
class PhoneNumber < ActiveRecord::Base
belongs_to :phonable, :polymorphic => true
end
class Website < ActiveRecord::Base
belongs_to :webable, :polymorphic => true
end
Having the above setup, you can get a collection of emails for a business instance via the call “@business.emails”. Similarly, you can get a collection of emails for an employee instance via “@employee.emails”.
Now, if you really think about what a contact information is? It basically contains a label and an information. I.e. Personal Email: te**@gm***.com , Business Phone Number: 1-800-999-9999, Business Website: www.google.com, etc.
So, we can say that an email, a phone number, or a website is an instance of a contact information class with two attributes: a label and an information. Thus, we can store any type of contact information in a single table.
In rails, if you name a column “type”, by default ActiveRecord allows inheritance by storing the name of the class in the column type. You can overwrite the Base.inheritance_column to change the name of the column where the class name is stored. Now, changing our polymorphic associations as “contactable”, we can have the following setup:
class Business < ActiveRecord::Base
has_many :emails, :as => :contactable, :class_name => "Email"
has_many :phone_numbers, :as => :contactable, :class_name => "PhoneNumber"
has_many :websites, :as => :contactable, :class_name => "Website"
end
class Employee < ActiveRecord::Base
has_many :emails, :as => :contactable, :class_name => "Email"
has_many :phone_numbers, :as => :contactable, :class_name => "PhoneNumber"
has_many :websites, :as => :contactable, :class_name => "Website"
end
class ContactInformation < ActiveRecord::Base
belongs_to :contactable, :polymorphic => true
end
class Email < ContactInformation
# any validation for email can go here
end
class PhoneNumber < ContactInformation
# any validation for phone numbers can go here
end
class Website < ContactInformation
# any validation for website urls can go here
end
All the emails, phone numbers and websites will now be stored in the table associated with the model “ContactInformation” and will have the following setup:
create_table :contact_informations, :force => true do |t|
t.string "contactable_type"
t.integer "contactable_id"
t.string "type"
t.string "label"
t.string "info"
t.timestamps
end
Now, when you create a business email:
@email = @business.build.emails(:label => "Personal", :info => " te**@gm***.com ");
@email.save!
the record will be saved in the table “contact_informations” with the values “Business” for the “contactable_type”, @business.id for the “contactable_id”, and “Email” for the column “type”.
Similarly, for employee phone number:
@phone = @employee.build.phone_numbers(:label => "Work", :info => "1-800-123-4567");
@phone.save!
the record will be saved in the table “contact_informations” with the values “Employee” for the “contactable_type”, @employee.id for the “contactable_id”, and “PhoneNumber” for the column “type”.
Rails makes it really easy to do a single table inheritance with polymorphic association.