Practical PHP Patterns: Concrete Table Inheritance
Join the DZone community and get the full member experience.
Join For FreeApart from Single Table Inheritance and Class Table Inheritance, there is a third strategy to map a set of classes which involve inheritance into a relational database, which does not support is-a relationships.
Concrete Table Inheritance is the name of the third strategy, and it consists in representing a hierarchy of classes with a set of tables, where every class has a 1-1 association with its own table in the database. But unlike in the Class Table Inheritance solution, there is no separation of the single object's data across multiple tables: every table contains all the fields that an object of the correspondent class has, without taking into account where they were defined.
The name of this pattern means that there is a table for every concrete class (every class you can create an instance of). In practice, this often means every class of the hierarchy, obviously ignoring interfaces. An alternative name and implementation is cited by Fowler in his PoEAA book: Leaf Table Inheritance, a pattern where every leaf class of the class diagram gets its own table. There are minor differences between the two versions of this pattern, but the latter only saves the overhead of a few tables.
In synthesis, this pattern simply ignores the hierarchy when defining the schema of the relational database, or while it's storing an object in it (the class name is mapped to the single table to touch in the process).
Advantages
There are some pros in using this pattern, due to its simplicity:
- the tables created for the object model make sense by themselves, and each of them is self-sufficient. This is an advantage particularly when you use the relaional database as a point of integration with other applications, which do not have access to the object model (for example because they are written in another language.)
- Due to the previous fact, there is no need for JOINs to be performed, so when you're looking for a single object the query will be pretty fast.
Disadvantages
- For starters, every field definition is duplicated as a column in all the tables that require it.
- The ORM cannot always ignore the multiple, parallel tables when querying and retrieving results: for example when querying the root class of a hierarchy you expect to have results from all the different subclasses. This means that multiple SELECT must be executed together on the ORM side, or on the database side with the UNION operator.
- Refactoring of fields (Push Down Field, Pull Up Field, new fields introduction and their deletion) always requires a change to the schema, often a change that propagates throughout the majority of tables.
- Primary keys are not unique by design, since they are usually unique only in a single table with the natural constraints supported by the rdbms. Either you use a different kind of key like a sequence table in the database or an UUID, or you lose uniqueness. The assumption here is that a primary key value should be unique throughout all the tables of the hierarchy.
- There are more cons in the Relationships section of this article.
Relationships
Foreign keys suffer of the same problem of primary ones: they are not checked in the database side for constraints (as they span over multiple tables).
The classic Car example goes like this: if Car is a class and Ferrari is its subclass, their tables should both have a engine_id column when mapping them with this pattern. But this means that a Car and a Ferrari object can refer to the same Engine in their database representation, with no foreign key constraints to help us from committing conflicting changes.
Thus, the Unit of Work should deal with much attention to these use cases (with Active Record you would simply lose any hope), and either the checks have to be implemented in the application code, losing the transparency, or by the ORM itself, but it can be time-consuming both for the developers and for the machine to execute all the chatty communication with the database.
Another inconvenience is the duplication of association tables, which results in also unconstrained many-to-many relationships.
Examples
Doctrine 2, the most famous PHP Data Mapper implementation, does not support this pattern at this time (while its support for inheritance comprehends the other two strategies). Roman, the project leader, said it could be a post-2.0 feature and it is tricky to implement (requiring UNIONS to query multiple tables at the same time), and will be implemented basing on demand from the users.
Thus, as an example, I will show a sample class hierarchy by its Uml class diagram (Uml is useful in the small to quickly explain the semantics of a set of classes) and the database schema generated by this pattern.
This is an object model where Vehicle has two subclasses, Van and Car. Vehicle is not abstract, so Vehicle objects can exist (without considering subclasses).
The relational model used by a Concrete Table Inheritance implementation would be:
Vehicle (oid, manufactures, model, color);
Van (oid, manufactures, model, color, wheels, capacity);
Car (oid, manufactures, model, color, seats);
Opinions expressed by DZone contributors are their own.
Comments