Tony Vincent
Tony Vincent

Reputation: 14262

Polymorphic Associations - rails

I have some trouble understanding polymorphic associations in rails. How is

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class Employee < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

different from

class Picture < ApplicationRecord
  belongs_to :employee
  belongs_to :product
end

class Employee < ApplicationRecord
  has_many :pictures
end

class Product < ApplicationRecord
  has_many :pictures
end

Upvotes: 1

Views: 669

Answers (4)

Prashant Ravi Darshan
Prashant Ravi Darshan

Reputation: 545

Without Polymorphic
20160902065429_create_employee_images
class CreateEmployeeImages < ActiveRecord::Migration[5.0]
    def change
        create_table :employee_images do |t|
            t.integer :employee_id
            t.string :image

            t.timestamps
        end
    end
end
20160902065445_create_product_images
class CreateProductImages < ActiveRecord::Migration[5.0]
    def change
        create_table :product_images do |t|
            t.integer :product_id
            t.string :image
            t.timestamps
        end
    end
end

app/model/employee.rb
class Employee < ApplicationRecord
    has_many :employee_images
end

app/model/product.rb
class Product < ApplicationRecord
    has_many :pictures, as: :imageable
end

app/model/employee_image.rb
class EmployeeImage < ApplicationRecord
    belongs_to :employee
end

app/model/product_image.rb
class ProductImage < ApplicationRecord
    belogs_to :product
end

With Polymorphic

db/migrate/20160902063459_create_products.rb
class CreateProducts < ActiveRecord::Migration[5.0]
    def change
        create_table :products do |t|
            t.string :name
            t.timestamps
        end
    end
end

db/migrate/20160902063513_create_employees.rb
class CreateEmployees < ActiveRecord::Migration[5.0]
    def change
        create_table :employees do |t|
            t.string :name
            t.timestamps
        end
    end
end

db/migrate/20160902063602_create_pictures.rb
class CreatePictures < ActiveRecord::Migration[5.0]
    def change
        create_table :pictures do |t|
            t.string :picture
            t.integer :imageable_id
            t.string :imageable_type
            t.string :image

            t.timestamps
        end
    end
end

app/model/employee.rb
class Employee < ApplicationRecord
    has_many :pictures, as: :imageable
end

app/model/picture.rb
class Picture < ApplicationRecord
    belongs_to :imageable, polymorphic: true
end

app/model/product.rb
class Product < ApplicationRecord
    has_many :pictures, as: :imageable
end

1- Without polymorphic. We have used two different tables to do the same thing. 2- With polymorphic we have create only a single model to store the images of two or more than two models in third polymorphic model.
Here we need two coulms in third table, one is for stroe model class name and second one for record id . we can take any two column but column name should be same like imageable+id imageable+type here we can use any name insted of imageable like we can use poly_id poly_type but we have to make sure what we are using in model.

if we use poly_id and poly_type then we have to use belogs_to :poly, polymorphic:true in picture.rb and has_many :pictures, as: :poly in employee.rb and product.rb both in both.

Upvotes: 1

Afsanefda
Afsanefda

Reputation: 3339

Imagin you have these models : Post,Idea,Article and you want to have comment model for all the three! You can have three tables for comments like : PostComment, IdeaCommnet, ArticleComment or you can have one General Comment Model and store each comment based on it's related model. As @Karan Purohit mentioned :)

Upvotes: 0

Karan Purohit
Karan Purohit

Reputation: 2659

In the second case you will need to add two foreign keys i.e employee_id and product_id in pictures table.

Where as in first case t.references :imageable, polymorphic: true in your pictures migration will add two fields in pictures table i.e

t.integer :imageable_id
t.string  :imageable_type

imagable_type field will be the name of class with whom you are associating this model to and imagable_id will hold the id of that record.

e.g,

Typical rows of picture table will look like

id | name | imagable_id | imagable_type |

1  | pic1 | 1           | Employee      |
2  | pic2 | 3           | Product       |

So here, Picture of first row will belong to Employee model holding the picture of Employee with id 1. Second row will belong to Product model holding the picture of product with id 3

Advantages of first Approach is you can associate picture model any other model in future without having to add foreign key to it.

Just add the line

has_many :pictures, as: :imageable

and association will be set.

Upvotes: 3

errata
errata

Reputation: 26792

There's not much difference other than the schemas and syntax required to support it. If you have some relatively large number of belongs_to :imageable relations it would begin to make sense as a use case. Using the standard naming approach they will reduce n number of fields necessary to represent multiple belongs_to associations to two, "MODEL_able" and an id that references the id of the targeted model. This is in favor to having a MODEL_id for each belongs_to model. It's fairly rare for them to be a huge win but a good thing to be familiar with.

Upvotes: 3

Related Questions