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');
}
}