DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

[DZone Research] Observability + Performance: We want to hear your experience and insights. Join us for our annual survey (enter to win $$).

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Understanding RDS Costs
  • NULL in Oracle
  • Schema Change Management Tools: A Practical Overview
  • The Evolution of EMR Software Development

Trending

  • Five Tools for Data Scientists to 10X their Productivity
  • Multi-Tenancy With Keycloak, Angular, and SpringBoot
  • The API-Centric Revolution: Decoding Data Integration in the Age of Microservices and Cloud Computing
  • The Stairway to Apache Kafka® Tiered Storage
  1. DZone
  2. Data Engineering
  3. Databases
  4. Practical PHP Patterns: Single Table Inheritance

Practical PHP Patterns: Single Table Inheritance

Giorgio Sironi user avatar by
Giorgio Sironi
·
Jun. 21, 10 · Interview
Like (0)
Save
Tweet
Share
2.80K Views

Join the DZone community and get the full member experience.

Join For Free

The idea behind the Single Table Inheritance mapping pattern is to represent an inheritance hierarchy (of entity classes) as a single table, which contains columns for all the fields of the various classes.

Inheritance and rdbms

This pattern is the simplest of the various strategies that can be employed to fit an inheritance-capable model into the relational one, which out of the box does not support it (tables which are children of others have never been seen in MySQL and other dbms.)

These mapping techniques are the same ones used in detailing an entity/relationship model into a logical one which does not understand inheritance. But that's for academics.

Object models promotes inheritance (to the point that it is often overused), and in PHP the extends keyword can be used for subclassing. Note that interface inheritance, even multiple, does not influence object-relational mapping since there is no additional data to store for the ORM when a class implements an interface.

I'm not a fan of inheritance and I favor composition in my models, as the Gang of Four suggests in their original book. However when dealing with applications that mostly shuffle data around (from the storage to the user interface, and the converse) inheritance can be handy if in the domain there are real is-a relationships and subclassing is not introduced only to reuse methods and avoid writing delegation or currying code.

Implementation

The Single Table pattern is characterized, of course, by one table that contains all the fields that the various classes (in the same hierarchy) define. This table is commonly named like the root class of the hierarchy. Not all objects define the whole set of fields available in the table: empty columns are used for values not present in a particular object's state, and the NULL value fills them.

Moreover, the table schema needs an additional field that someway encodes the class name (discriminator column), in order for the ORM to know which class to instantiate when recreating an object from the storage after queries or simple find*() calls.

The discriminator column can contain special codes (enums) or simply the class name of the object, since space is not an issue nowadays. Defining a code for every class is thus an aid for refactoring since it eliminates duplication (you can change the name of the class without updating the code in the schema) and it can be useful for interoperability with other applications that access the relational database directly. Ideally you will never see these class codenames in application code.

Pros & cons

There are some advantages of this pattern over other inheritance mapping strategies. As defined by Fowler:

  • a single table contains every data of the hierarchy, in a cohesive way.
  • There are no SQL joins for recreating a single object, which is not true for some other strategies.
  • Pull Up Field and Push Down Field refactorings do not require change to the schema, which may result in data migration scripts when pushing the update in production.

Of course this pattern is a trade-off, and presents also disadvantages:

  • not all the columns are relevant for a particular row (conceptual waste in the relational schema).
  • There is unused space due to all the NULL values in non-relevant columns (space waste).
  • The table can get very large in deep hierarchies (this can be a sign of refactoring needed to avoid deep hierarchies.)

So when we should use this pattern?

Usually when there is a stable hierarchy of classes, and there are no frequent additions of subclasses in the domain model. Adding a subclass results in every supplemental field added to the schema of the single table.

Another use case is in case that high performance is required. Other strategies may require different tables to store fields of the same object, even when it does not have relationships.

And when we should not use this pattern?

When we want to provide entities as subclasses of a class from another package. The mapping information are kept on the uppermost superclass, so if it comes from a vendor package and you subclass it you can't modify the mapping metadata. The trade-off is that you can provide all the info in one place, but then can't add a class to the hierarchy without modifying another one (the root). This kind of strong coupling defeats some of the purpose of inheritance from a modelling point of view, but we can argue that mapping metadata is part of infrastructure and not of the model itself.

You should avoid this pattern also when there are very different fieldsets in classes in the same hierarchy, that render the single table large in columns count and very clumsy.

Examples

Support for inheritance has been a killer feature of the Doctrine ORM in its version 1. In version 2.x, it is implemented with respect to the JPA specification (borrowed from Java).

In this article I'll provide a bit of sample code in which the metadata are defined with annotations. Here @DiscriminatorMap defines an associative map of enumerative values that point to class names, which is the primary point of reference to subclasses in the root one.

As you can see, all the metadata are on the root class, and there is no specific metadata on inheritance strategies on subclasses (they only need generic metadata like @Column, @OneToOne annotations.) This way, class-specific metadata like column types are still kept on the relevant class, even if the informations on the whole hierarchy are on the root, in the only place that makes sense.

<?php
namespace MyProject\Model;

/**
* Example taken from the manual.
* @Entity
* Define Single Table Inheritance as the mapping strategy for this whole hierarchy:
* @InheritanceType("SINGLE_TABLE")
* Define the discriminator column:
* @DiscriminatorColumn(name="discr", type="string")
* Define a map of keys (which will be repeated in the various rows) and values (class names):
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}

/**
* @Entity
* Define annotations as if Employee does not extend anything.
*/
class Employee extends Person
{
// ...
}
Relational database Database Inheritance (object-oriented programming) PHP

Opinions expressed by DZone contributors are their own.

Related

  • Understanding RDS Costs
  • NULL in Oracle
  • Schema Change Management Tools: A Practical Overview
  • The Evolution of EMR Software Development

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: