Polymorphic Association with has_many :through
Hi Folks,
In Rails we have Ploymorphic Association whenever we need to connect one model to more than one model. I had a situation where i need polymorphic association with has_many :through <!--more-->
Here I am showing an simple example of polymorphic association with hasmany :through
We have Contact , Plan and Template as a ActiveRecord Classes.
We have following scenarios.
Plan hasmany ContactsContact hasmany PlansTemplate hasmany ContactsContact has_many Templateswhich is vice versa of 3.
So here is the main problem. Here to solve this we need two more tables other then Plans , Contants and Templates.
My Plan Model looks like:
{% codeblock lang:ruby%} class Plan < ActiveRecord::Base hasmany :plancontacts hasmany :contacts ,:through => :plancontacts end {% endcodeblock %}
My Template Model looks like:
{% codeblock lang:ruby%} class Template < ActiveRecord::Base hasmany :templatecontacts hasmany :contacts ,:through => :templatecontacts end {% endcodeblock %}
My Contact Model looks like:
{% codeblock lang:ruby%} class Contact < ActiveRecord::Base hasmany :templatecontacts hasmany :plancontacts
hasmany :templates ,:through => :templatecontacts hasmany :plans ,:through => :plancontacts
end {% endcodeblock %}
And here are two different tables for handling manytomany
Handling Plan and Contact here.
{% codeblock lang:ruby%} class PlanContact < ActiveRecord::Base belongsto :plan belongsto :contact end {% endcodeblock %}
Handling Template and Contact here.
{% codeblock lang:ruby%} class TemplateContact < ActiveRecord::Base belongsto :template belongsto :contact end {% endcodeblock %}
This problem can be solved using only one table. Just we need to mark that table as a polymorphic.
We are calling that tables as contact_details. Here is class looks like.
{% codeblock lang:ruby%} class ContactDetail < ActiveRecord::Base belongsto :contactable, :polymorphic => true belongsto :contact end {% endcodeblock %}
Migration should look like:
{% codeblock lang:ruby%} class CreateContactDetails < ActiveRecord::Migration def change createtable :contactdetails do |t| t.integer :contactid t.integer :contactableid t.string :contactable_type t.timestamps end end end
{% endcodeblock %}
Now change your Contact model like:
{% codeblock lang:ruby%} class Contact < ActiveRecord::Base hasmany :plans ,:through => :contactdetails, :source => :contactable, :sourcetype => 'Plan' hasmany :templates ,:through => :contactdetails, :source => :contactable, :sourcetype => 'Template' hasmany :contactdetails acceptsnestedattributes_for :plans,:templates end {% endcodeblock %}
Now change your Plan model like:
{% codeblock lang:ruby%} class Plan < ActiveRecord::Base hasmany :contacts,:through => :contactdetails hasmany :contactdetails, :as => :contactable end {% endcodeblock %}
Same way Template model like:
{% codeblock lang:ruby%} class Template < ActiveRecord::Base hasmany :contacts,:through => :contactdetails hasmany :contactdetails, :as => :contactable end {% endcodeblock %}
So you can see we can use one table(contact_details) for both plan and template
{% codeblock lang:ruby%} contact = Contact.create(:name => 'Rays') template = Template.create(:name => 'template 1') plan = Plan.create(:name =>'plan')
contact.plans << plan contact.templates << template
template.contacts => [contact] plan.contacts => [contact] contact.templates => [template] contact.plans => [plans] {% endcodeblock %}
so if you have acceptsnestedattributesfor :template then you can create template for contact in a form
{% codeblock lang:ruby%} Contact.create(:name => 'abc',:templatesattributes => {'0' => {'name' => 'Tech'}}) {% endcodeblock %}it will create an entry in templates table and and entry in contact_details
So in this way we dont need to write much code.we can reduce tables number
I hope it will be helpful for you
Reference links :