Inheritance Mapping

The SQLMap PHP DataMapper supports the implementation of object-oriented inheritance (subclassing) in your object model. There are several developer options for mapping entity classes and subclasses to database results:

You can use the most efficient mapping strategies from a SQL and query performance perspective when using the inheritance mappings of the DataMapper. To implement an inheritance mapping, the resultMap must define one or more columns in your query's resultset that will serve to identify which resultMap should be used to map each result record to a specific subclass. In many cases, you will use one column value for the DataMapper to use in identifying the proper resultMap and subclass. This column is known as a discriminator.

For example, we have a table defined in a database that contains Document records. There are five table columns used to store Document IDs, Titles, Types, PageNumbers, and Cities. Perhaps this table belongs to a legacy database, and we need to create an application using this table with a domain model that defines a class hierarchy of different types of Documents. Or perhaps we are creating a new application and database and just want to persist the data found in a set of related classes into one table. In either case, the DataMapper's inheritance mapping feature can help.

CREATE TABLE Documents (
    Document_ID int NOT NULL ,
    Document_Title varchar(32) NULL ,
    Document_Type varchar(32)  NULL ,
    Document_PageNumber int NULL  ,
    Document_City varchar(32)  NULL
)

To illustrate this, let's take a look at a few example classes shown below that have a relationship through inheritance and whose properties can be persisted into our Documents table. First, we have a base Document class that has Id and Title properties. Next, we have a Book class that inherits from Document and contains an additional property called PageNumber. Last, we have a Newspaper class that also inherits from Document and contains a City property.

class Document 
{
    public $ID = -1;
    public $Title = '';
}

class Book extends Document
{
    public $PageNumber = -1;
}

class Newspaper extends Document 
{
    public $City = '';
}

Now that we have our classes and database table, we can start working on our mappings. We can create one <select> statement that returns all columns in the table. To help the DataMapper discriminate between the different Document records, we're going to indicate that the Document_Type column holds values that will distinguish one record from another for mapping the results into our class hierarchy.

<select id="GetAllDocument" resultMap="document">
   select
     Document_Id, Document_Title, Document_Type,
     Document_PageNumber, Document_City
   from Documents
   order by Document_Type, Document_Id
</select>

<resultMap id="document" class="Document">
  <result property="Id" column="Document_ID"/>
  <result property="Title" column="Document_Title"/>
  <discriminator column="Document_Type" type="string"/>
  <subMap value="Book" resultMapping="book"/>
  <subMap value="Newspaper" resultMapping="newspaper"/>
</resultMap>

<resultMap id="book" class="Book" extends="document">
  <property="PageNumber" column="Document_PageNumber"/>
</resultMap>

<resultMap id="newspaper" class="Newspaper"  extends="document">
  <property="City" column="Document_City"/>
</resultMap>

The DataMapper compares the data found in the discriminator column to the different <submap> values using the column value's string equivalence. Based on this string value, SQLMap DataMapper will use the resultMap named "Book" or "Newspaper" as defined in the <submap> elements or it will use the "parent" resultMap "Document" if neither of the submap values satisfy the comparison. With these resultMaps, we can implement an object-oriented inheritance mapping to our database table.

If you want to use custom logic, you can use the typeHandler attribute of the <discriminator> element to specify a custom type handler for the discriminator column.

<resultMap id="document-custom-formula" class="Document">
    <result property="Id" column="Document_ID"/>
    <result property="Title" column="Document_Title"/>
    <discriminator column="Document_Type" typeHandler="CustomInheritance"/>
    <subMap value="Book" resultMapping="book"/>
    <subMap value="Newspaper" resultMapping="newspaper"/>
  </resultMap>
</resultMaps>

The value of the typeHandler attribute specifies which of our classes implements the ITypeHandlerCallback interface. This interface furnishes a getResult method for coding custom logic to read the column result value and return a value for the DataMapper to use in its comparison to the resultMap's defined <submap> values.

class CustomInheritance implements ITypeHandlerCallback 
{
    public function getResult($type)
    {
        if ($type=="Monograph" || $type=="Book")
            return "Book";
        else if ($type=="Tabloid" || $type=="Broadsheet" || $type=="Newspaper")
            return "Newspaper";
        else
            return "Document";
    }
    
    public function getParameter($object)
    {
        throw new Exception('unimplemented');
    }
    
    public function createNewInstance()
    {
        throw new Exception('unimplemented');
    }
}