This was a question from @photonerddan during class, and after looking through many examples online approaching it from an inheritance stance, as well as ones with examples that seemed outside of common use-cases, Mike and I decided to approach it from a problem / solution standpoint, which looks at it from a database perspective.
We will cover it more in-depth during the second week of Rails MakerSquare, but for now here is a short example to highlight the problem that polymorphism is solving in your database.
So let's start with the problem it is solving:
Polymorphism keeps you from having excessive foreign key columns in a database table where multiple things, such as notification feed items, are owned by other objects.
**Polymorphism helps solve the problem of creating a
- talk about the issue of multiple inheritance on the model side
- talk about doing it without
The problem is your trying to create an association with mulitple inheritance.
For example you want ot have a relationship between users and posts, photos, and other items, through notificaitons.
The initial solution that
If you ever have a database table where you are using mulitple columns for the purpose of providing a foreign key to a common shared object amongst those multiple columns, ... then there is a solid chance that you have a fragile database structure. Your database table might look like this:
(this)
Say you have a notifications feature for a user like on popular social networking sites, where it pulls in all types of media:
You might have tables for photos
, videos
, and posts
, and of course you have a users
who own all the things in these tables. Then if you want to have notifications for all those items when, for example they are created, you have a notifications
table with a column telling you if a user has seen that notification.
users
table:
id | username |
---|---|
1 | slaney98 |
2 | jokester71 |
3 | mzuckdog |
4 | yoyoyoyo1 |
The user_id
would be a foreign key in this instance back to the user who posted the photo
, video
, and post
, respectively.
photos
table:
videos
table:
id | video_shortcode | user_id |
---|---|---|
1 | GnO7D5UaDig | 4 |
2 | FFtKLISfPE | 4 |
3 | vM40X2cvQ | 2 |
4 | TGLxjppFqeA | 1 |
posts
table:
id | content | user_id |
---|---|---|
1 | "This is a post" | 3 |
2 | "This is also a post" | 4 |
3 | "This is a post too?" | 1 |
4 | "This is the coolest social network!!" | 1 |
If I wanted to add a notifications
table without Polymorphic associations, then my table structure would end up looking something like:
id | feedable_type | photo_id | video_id | post_id | ... |
---|---|---|---|---|---|
1 | photo | 2 | ... | ||
2 | video | 4 | ... | ||
3 | post | 1 | ... | ||
4 | video | 1 | ... |
If you wanted to add another media type, then this structure becomes more brittle. You would keep adding columns, and it doesn't seem like an elegant solution or concise form of data storage.
You would want that notification
record to have a foreign key of photo_id
, video_id
, and post_id
. Depending on the amount of media types this could be a lot of extra foriegn key columns.
Lets take a look at what we can do to make the notifications
table more malleable to different media types, as well as use a more scalable data storage technique with less waste.
The general pattern should be to start with the model that will be shared between the other models -- in this case, notifications
. We'll first set the notification
model to belong to notifiable
, and then our other models (picture
, video
, and post
) will then all has_many :notifications, as: :notifiable
, which is essentially multiple inheritance.
This code is illustrated here:
# app/models/notification.rb
class Notification < ActiveRecord::Base
belongs_to :notifiable, polymorphic: true
end
# app/models/picture.rb
class Picture < ActiveRecord::Base
has_many :notifications, as: :notifiable
end
# app/models/video.rb
class Video < ActiveRecord::Base
has_many :notifications, as: :notifiable
end
# app/models/post.rb
class Post < ActiveRecord::Base
has_many :notifications, as: :notifiable
end
Now, your notifications table data structure can be much simpler. You can consolidate the first part of the name of each foreign key into the notifiable_type
column. Then notifiable_id
holds the id
of the foreign key in concept.
id | notifiable_type | notifiable_id | user_checked |
---|---|---|---|
1 | photo | 2 | true |
2 | video | 4 | true |
3 | post | 1 | false |
4 | video | 1 | true |
An ounce of dry code is worth hours of work and many fewer headaches later. We hope this post can help clarify Polymorphic associations by looking at the database problem it solves.