Single table inheritance with Eloquent (laravel 4)

Eloquent is a great orm. It provides a very usable api powered by very clean, readable code. Eloquent opens itself up to extension, thus allowing you to implement powerful patterns over it that help you retain the power of an orm while maintaining a proper separation of concerns.

One of such patterns that I am going to discuss about in this blog post is Single Table Inheritance with Eloquent. This pattern is used whenever you need to store entities in a single table, which have the same fields, but different behaviors. For example, different types of orders like delivery and pickup. Or different types of users for a school system, like student, teacher, official etc. Each of these entities have certain common behaviors and relationships, while the sub-types can have new behaviors and relationships of their own.

So how do you go about implementing this ? You create a base model which extends eloquent and overrides a few eloquent methods and adds 2 small methods.

The trick being used here is that the Eloquent query builder uses the method newFromBuilder to build models from the data it has just received. We override that method in our base class and pass on the responsibility of deciding the class whose instance has to be filled by the retrieved attributes.

We also create a new newRawQuery method and use it for saving because then we can override newQuery to set up default scope on models.

Below Is a use case of this setup that I am personally using in a project.

We ensure via validations that only proper type is inserted in the orders table, and since mapData is called only when building models from eloquent-query-builder, we can safely throw an exception if $type does not match any of the expected values.

And once you have this setup, you can reap the benefits of single table inheritance pattern in modelling your business logic. You can do an Order::all() and the models that you receive will be instances of the appropriate sub-class based on the attributes of the row they represent. ie, if a row in your orders table has type=’newDelivery’, then according to that setup, it would be instantiated as a OrderNewDelivery object.

UPDATE : Forgot to mention but this setup also works for relations to the Order class. That is, suppose a Customer has-many Order , then when you do $customer->orders, the orders fetched for the customer would be of appropriate class. You just need to form the relation to the model which defines the mapData method to set-up single table inheritance.

Note that the Order class functions a lot like an abstract class, but it cannot be an abstract class because of the way eloquent works. Thus, if you need to enforce abstract methods in your base class, i recommend using BadMethodCallException. For example:

The AbstractEloquent class provided can easily act as your base model, as the mapData method just provides a new-instance of the class being worked with by default.

This single table inheritance pattern has allowed me to distribute my business logic across my models in a very clean and intuitive manner.

Don’t forget to give a shout out in the comments if you use it after reading this article, would like to see how others are taking advantage of it.

Cheers!!

Pallav Kaushish
Pallav Kaushish
A marketing junkie fascinated with the amalgamation of marketing and psychology. I work with startups to boost their growth. Always happy to share ideas and learn from others. Feel free to drop me a note via about page.
Related Posts
  • Pingback: Single Table Inheritance with Eloquent (laravel...()

  • Thomas Clarkson

    Cheers for writing this article, it is very insightful and timely for me. As you noted the single table inheritance pattern should be used when all the data is the same for the different types of enties but if you wanted to store different data for different types of entities you could use polymorphic associations. I did an example of this here http://paste.laravel.com/yNg

    • kapil verma

      Hi Thomas, glad you liked the article !! (I have just started blogging so would be great if you promote it a bit 😛 😛 :D)

      The Single-Table-Inheritance pattern is used when your models really are very similar data and relation-wise, but have different business logic. And i really think the setup i have provides a cleaner api than polymorphic relationships.

      I personally would use polymorphic relationships in a different type of scenario that the one described in this article. In fact, one of my laravel 3 apps implement what polymorphic relations do in a hackish way to maintain a common registry of several different type of models (each with different tables) in the system. This registry is then used to assign memberships to users on these models.
      So essentially, by using (sort of) polymorphic relationships, I was able to implement access-control of users over entities from different tables, while avoiding the redundancy of managing data across 4-5 different tables, by using the registry as a single point of control.

      • Thomas Clarkson

        With the promition twitter is quite handy, as I saw this article from a tweet by laravelnews.. I tweeted to @DriesVints and he showcased the article on laravel weekly :) See http://laravel.io/topic/35/laravel-weekly-15

        I saw wondering if it would be a good idea if you could remove the mapData function and the switch statements and have it generate automatically like the laravel polymorphic association does because when you want to use single table inheritance in another model you will have to write the mapData function?

        • kapil verma

          Thanks a ton man !!

          as for the mapData function, its the simplest implementation. I can think of a few more, will share them in a day or two (too much workload :/), will share them for your review

          • Thomas Clarkson

            Hi Kapil,

            Did you get a chance to look into them or any ideas I can look into. Thanks :)

          • kapil verma

            Yes I did, I just need to integrate that thing with my dataMap thingy because a lot of code is written over it :p , give me few minutes, i’ll send a paste of it

          • kapil verma

            Hey .. so sorry for completely forgetting about this, too much workload , but here is an example use-case : https://gist.github.com/kapv89/6100738 , and here is the implementation of the class : https://gist.github.com/kapv89/6100725 . This is not tested, hence there can be a few syntax errors (but i don’t think there really are any). If you face any problems, just shoot a comment.

  • Thomas Clarkson

    Is there a way you could create a new relationship for Single Table Inheritance so you could just define the singletableinheritance relationship in the order models instead of having the map data logic in every model where you wanted single table inheritence

    • kapil verma

      Hey .. I just added an update just before the last code-block in case you are worried about how would relationships work in this setup. Check out if that works for you.

      Also, do note that AbstractEloquent isn’t something concrete, you can obviously overwrite the api explained in here to make an api that suits you best. The main idea is that eloquent-query-builder uses the method “newFromBuilder” in eloquent to instantiate Eloquent models from table-row-data. That what you need to play around with.

      Hope this helps !!

  • rydurham

    I ran into a problem where the timestamps on my sti models were not being update after saving (or touching) the model. I was able to resolve this by setting the “sync originals” flag to true in the newFromBuilder() function:

    $m->setRawAttributes((array) $attributes, true);