Converting to Polymorphism

Berel Levy
3 min readAug 19, 2020
sorry

For my Mod 3 project, I made an eCommercesque app. when I got to the stretch goal of adding purchasing capability, I noticed that my join table for items and purchases was identical to my join table for item and cart. additionally, whenever I would need to create a purchaseItem, I would have to also destroy the corresponding CartItem.

This got me thinking, what if I just made the cartItem polymorphic?

my schema looks something like:

Cart -< CartItem >- Item

My purchase would look like:

Purchase -< PurchaseItem >- Item

I won’t go into too much detail, but I began to realize that Purchase would share many methods with Cart, and PurchaseItem would share all the methods with CartItem. More than that, I realized that when I created a purchase out of a cart, I would be deleting the CartItems while creating the new PurchaseItems. So, why not use CartItems for purchases as well? Then I would be able to just switch the cartItem from referencing the cart to referencing the purchase, instead of needing to delete the existing cartItem while also making a new purchaseItem

To do so, the cart/purchase reference field in cartItems would need be polymorphic

What is Polymorphism?

Well, CartItem is a join table. On one end, CartItem joins to Item, on the other end, CartItem joins to Cart, or purchase. Huh? that’s right, every cartitem can can reference a cart or a purchase.

Disney all rights reserved

How do you do that?

Short version: replace the cart_id field in cartitem with two fields, itemable_id and itemable_type. itemable means ‘can be referenced by an item’, the name of the table referenced goes in itemable_type.

Next, use the gifts bequeathed to us by ApplicationRecord to make expoliting that relationship a breeze.

Long version: implement it!

First let’s add the polymorphic field to our model. Let’s generate a migration, which should look like this:

Now cart Item can be related to any other model/table. so for the big picture some validation here would be in order.

now to add the methods to cart and purchase

See, the methods are exactly the same!

Since this app is and will always be, in dev, I just need to remove the seed data before I could remove my cart. However, in production you would need to migrate the data from cart_id to itemable_id (and set itemable_type to ‘Cart’). The method would look something like this:

CartItem.all.each do |ci|
ci.itemable = ci.cart
ci.save
end

once your data has been migrated you can now safely delete the cart_id field from cartItem. See the code below hosted with Unicode U+2764 ️ U+FE0F by github.

once this change is made TEST ALL YOUR METHODS IN CART AND CARTITEM. this is a huge change, so start with the most basic methods and work your way up.

In our case, All of our Cart methods still work, since cart still knows cartitem as cartitem, and cartitem doesn’t have any methods that call on cart, so we are good there.

now to write the code to create the purchase.

Lines 5 thru 7 do the lifting, and as you can see, it’s not that heavy.

--

--

Berel Levy

Experienced software engineer and data engineer who enjoys the finer things in coding.