Polymorphic Associations in Rails
Tags: ruby, rails, database
Published: 2021-09-08 18:32:56

When setting up an API in Rails it is crucial to establish how all of your models are going to be connected to each other using [__Active Record Associations__](https://guides.rubyonrails.org/association_basics.html).  Polymorphic associations are a little more advanced but can be very useful when your application becomes more complex.


##What is Polymorphism?
This is a new concept for me and so far I have learned that polymorphism is a core concept in Object-Oriented-Programming that can be used in many different contexts.  This blog will be focusing on this concept in the context of building a Rails API.  We can get a more clear understanding of this concept by breaking it down into its _Greek_ roots:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2jk3tep3cdd3fvdk8t4.png)
If something is polymorphic it has many forms.  For example, `numbers` in Ruby can be `integers`(whole #'s), or `floats`(decimal #'s).  A polymorphic association in Rails allows you to associate a single model with multiple models through one association declaration (instead of having multiple `belongs_to` associations).


##Where could this be useful?
Let's say you are building an app that allows users to post content, comment on other posts, and like other posts.  Pretty common features that we are all familiar with.  Now you want to extend the like feature to comments allowing a user to like individual comments.  Your ERD might (or might not) look something like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x1gboqhtub5j2kvrkve1.png)
Something like this would work fine, but if you wanted to extend the like feature throughout your app to potential new models in the future you would need to create a separate likes table to the database for each new model you introduce.  Overtime this could unnecessarily crowd your database and will end up becoming a __repetitive__ task.


##Polymorphic associations
With polymorphic associations we can create a _poly_likes_ table that will act as an __interface__ between other models we want to associate likes to.  This isn't the greatest looking ERD but it is looking a little better.  Now everytime we want to introduce a new model, we don't need to create a separate likes table to go with that model.  
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwjpdacxy5g3kltgnkw2.png)


##Creating a poly_likes table
To create our new model in the terminal we are going to include an interesting column/value pair.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdz4k86q5is3n9x761xt.png)
####What does the migration show us?
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xripx39pxp683ijnm7n5.png)
####Lets run the migration and check out the schema
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yo8usygvfo442aneeste.png)
####What about the model?
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ss4drf850mxf0tps0nrg.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s40yohyisbyfk1mihjlr.jpeg)
If you're seeing all of this for the first time the syntax might seem a little weird.   Why are our associations named with "-able"?  


##Naming convention for polymorphic associations
If you're familiar with Rails you know that sticking to naming conventions is part of Rails' magic.  To make all of this work, Rails uses "-able" with the class name to name polymorphic associations.  Basically what we are doing here is making a comment and a post "likeable".  Let's make sure the rest of our models are set up to make all of this work:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zhjj7mk6nesjhkmuja9g.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7lzpom6d2tt959ypgra.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0x2trs9vqizkmrggu9r.png)
PLEASE correct me if I am wrong, but I left out the "as: likeable" from the _user_ model because that would make a user likeable which is not what I want in this situation (who knows maybe you want to allow a user to be liked).  In this situation I just want a _poly_like_ to be associated with a specific user.


##Testing it out in the console
If you take a close look at the query being generated it is looking for a _poly_like_ that has a _user_id_ which is what I wanted.  In these examples I did not create any poly_like seeds so we are getting back an empty array, but we can focus on the queries to see how these associations are working.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mw202fxo2ja7j9mykgh3.png)
####Now let's check a comment and a post
If we look at the queries again we can see that the _likeable_id_ matches the id of the respective object and the _likeable_type_ references the model __type__ in the form of a string!
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ern8ihg0ym7kwo8gg8t.png)


##Pros
- Keeps our code DRY by not having to use a bunch of repetitive `belongs_to` or `has_many` associations across all of our models.
- Prevents you from needing to modify existing models and/or adding new join tables to create associations with newly introduced models.  (Ex: we did not have to add a separate likes table for the comment & post models like we did in the first ERD.)
- Makes it easier to add new relationships.


##Cons
- Syntax could take some getting use to at first.
- No foreign keys.


##Conclusion
Polymorphism might be a little tricky to wrap your head around at first, but it is really just a fancy word for saying an __object__ can be related to many __different__ __types__ of objects.  In my short experience with polymorphism I can see why this a powerful concept but it is probably not necessary for every situation.  I am not an expert on this topic and I used this blog as an opportunity to learn an important concept.  Please let me know in the comments below if there are any corrections I need to make to this post or if there are some other practical use cases where polymorphism could really come in handy.  Hopefully this post made polymorphism more approachable for you.  I linked some very helpful resources below that really helped give me a fundamental understanding.

##Resources
https://www.youtube.com/watch?v=pTB0EiLXUC8
https://www.youtube.com/watch?v=SjUVEUCMoX8
https://www.youtube.com/watch?v=5EidzTqsw-E&t=620s
[Polymorhpic associations](https://guides.rubyonrails.org/association_basics.html#polymorphic-associations)
[freeCodeCamp blog on polymorphic associations](https://www.freecodecamp.org/news/single-table-inheritance-vs-polymorphic-associations-in-rails-af3a07a204f2/)
[Stackoverflow polymorphism](https://stackoverflow.com/questions/1031273/what-is-polymorphism-what-is-it-for-and-how-is-it-used)