Learn about STIs and Polymorphic.

Tram Ho

If you have ever created an application with more than one model, you will need to think about the type of relationship to use between the models. As application complexity increases, it will be difficult to determine which relationships should exist between your models? The common situation is when some of your models need access to the 3rd model’s functionality. The two methods that Rails provides to solve this problem are Single-table inheritance (STI) and Polymorphic. association.

  • With Single-table inheritance (STI), multiple subclasses will inherit from a parent class with all the data in the same table in the database. The parent class has a column of “type” to determine which subclasses belong to an object.
  • With Polymorphic association, a model can belongs_to to one or more other models using a single link. Each model, including polymorphic model, has its own table in the database. Now let’s look at each of the methods to determine when to use them.
    Single-Table Inheritance
    The way to know when to use an STI is when your models have shared data or state (data / state). And sharing behavior is optional. Let’s say we need to create an application that lists the different vehicles sold at a dealership. The products we have are cars, motorcycles and bicycles. For each vehicle, the dealer wants to keep track of the prices, colors, and whether those vehicles were purchased. For this example the application of an STI is the right choice, as each dealership car is concerned with features such as price, color, and whether or not sold. We can use the same data for each object. We will have the parent class Vehicle with the color, price, purchased properties. Each subclass (cars, motorbikes, and bicycles) can inherit from Vehicle and all can get the same attributes. The migration to create the Vehicle table would probably look like this:

The important thing when applying STI is that we have to create type column for superclass. This tells rails that we are using an STI and wants all the data for Vehicle and its subclasses to be in the same table in the database.

Our model classes will look like this:

This approach is great because any method or authentication in the Vehicle parent class is shared with each of its subclasses. We can add unique methods to any subclass if needed. They are independent of each other and their methods cannot be shared. Also, since we know that subclasses share the same data fields, we can make the same calls on objects from different classes:

Adding functionality
For now, we will assume that the dealer decides to collect more information about the vehicles. With Bicycles , they want to know if each cycle is a road bike, mountain bike or hybrid bike. For Cars and Motorcycles, they want to keep track of horsepower. We will create a migration to add the bicycle_type and horsepower column to the Vehicle table. With Bicycles, they want to know if every bike is a road, mountain or hybrid. And Cars and Motorcycles they want to keep track of horsepower. We will create a migration to add the bicycle_type and horsepower column to the Vehicle table. And it’s evident that our models no longer share the data perfectly. Bicycles objects do not need a horsepower property nor do Cars and Motorcycles need a bicycle_type property. However, practically each bicycle in the data sheet still has a horsepower field and each car or motorcycle will also have a bicycle_type field. So, problems will arise:

  1. Our table will have lots of null values ​​(nil in Ruby’s case) because the objects will have fields that don’t apply to them. Null values ​​can be problematic when we add validations to their models.
  2. As the table data gradually increases, query performance costs may incur if filters are not added. Searching for some bicycle_type will have to look for every record in the table even though only in Bicycles , and not Cars and Motocycles .
  3. As such, there is nothing stopping the user from adding “inconsistent” data to the wrong model. Example: A user can create a Bicycles with horsepower =1000 . We’ll need good validation and application design to prevent the creation of an invalid object. So we can see that the STI has some flaws. It’s great for applications where your model shares data fields and is not subject to change.
    Polymorphic Associations
    With Polymorphic Associations, a model can belong to several models with a single association. When some models do not have relationships or share data with each other, but there is a relationship with the polymorphic class. For example, think of a social networking site like Facebook. On Facebook, both individuals and groups can share articles. The kernels and groups are unrelated and the data types are different. A group can have member_count and group_type not applicable to a kernel. Where do not use polymorphic links.

Normally, to find out who owns a certain record, we look at that column, foreign_key . Foreign_key is an id used to find the related object in the related model’s table. However, the Post table will have two foreign keys: group_id and person_id . This will be problematic. When trying to find the owner of a post, we’ll have to check both columns to find the exact foreign key, instead of relying on one. What if we run into a situation where both columns have a value? Polymorphic linking solves this problem by aggregating this functionality into a link. We can represent our classes like this:

The Rails convention for naming a polymorphic association uses “-able” with the class name ( :postable for the Post class). This makes it clear in your class relationships what polymorphism is. But you can use any name for the polymorphic link you want. For databases, we need 2 columns type and id for this polymorphic class. The postable_type column records the model the post belongs to, while the postable_id column tracks the id of the owner object:

A polymorphic link is only a combination of two or more belongs (belongs to) links. Therefore, you may act the same way you would when using two models with a belongs to link. Note: Polymorphic links can work with both has_one and has_many

One difference is going backwards from a post to its owner access, since its owner could come from one of the classes. To do that quickly, you need to add a foreign key column and type column to the polymorphic class. You can find the owner of a post using the postable :

Summary
Single-Table Inheritance and Polymorphic associations, although not the only solution to tree-like relationships, both have advantages and disadvantages. Both the Vehicle and Postable examples can be deployed using both methods. However, there are several reasons to distinguish which method is best in the situation.

  1. Database structure: STI uses only one table for all classes in a relationship, while polymorphic associations use one table per class.
  2. Shared data or state: STI is a great choice if your models have multiple shared properties. If not, polymorphic associations is probably a better choice.
  3. Future concerns: Consider how your application can change and develop. If you’re looking at an STI but think you’ll add other models or model fields with a shared structure, you may want to rethink your plan. If you think your structure is likely to be preserved, the STI will generally be faster to query.
  4. Data integrity: If an application uses your database, polymorphic links are probably a bad choice because your data will be compromised.
    References
    https://www.freecodecamp.org/news/single-table-inheritance-vs-polymorphic-associations-in-rails-af3a07a204f2/#:~:text=In Single-Table Inheritance (STI, models% 20using% 20a% 20single% 20association.
Share the news now

Source : Viblo