Home arrow PHP arrow Page 2 - Roll Your Own Repository in PHP: Defining a Generic Data Mapper

The mapping layer - PHP

In this third part of the series, I add a simple mapping layer to the sample application. This layer will be composed of a single interface and a generic mapper class.

TABLE OF CONTENTS:
  1. Roll Your Own Repository in PHP: Defining a Generic Data Mapper
  2. The mapping layer
By: Alejandro Gervasio
Rating: starstarstarstarstar / 4
November 29, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

Constructing the mapping layer is a two-steps process. First, it’s necessary to define a contract (pretty similar to the one used with the database adapters discussed previously), which must be satisfied by all of the mapper classes created afterward. And second, the generation of these classes will be achieved by subclassing a generic mapper, even though this last step is optional.

With that said, here’s the interface that defines the structure of the aforementioned contract:

(DataMapperInterface.php)

<?php

interface DataMapperInterface
{
    public function findById($id);
   
    public function findAll();
   
    public function search($criteria);
   
    public function insert(EntityAbstract $entity);
   
    public function update(EntityAbstract $entity);
   
    public function delete($id);         
}

Although the definition of the above “DataMapperInterface” interface is pretty short, it’s clear to see that it declares a bunch of methods that can be used for performing CRUD operations against the data access layer. Apart from the classic finders that can be found in most mappers, like “findById()” and “findAll(),” there are a few additional methods that allow you to save and delete records associated with a specified entity. Since the role of an interface is to define a contract, and nothing else, it’s up to the mappers to concretely implement these entity-related operations. Got that? Great.

Now that you understand the purpose of defining the earlier interface, the next thing that we need to do is create a class that implements the interface in question, and also encapsulates common functionality shared by concrete mapping classes. This will be done in the section below, so keep reading.    

Implementing the previous interface: defining a generic mapper

In keeping with the concepts deployed above, the last step we must take to get the mapping layer of this sample application finished is to build a generic mapper class. For obvious reasons, this class will be declared abstract, and its initial definition will be as follows:

(DataMapperAbstract.php)

<?php

abstract class DataMapperAbstract implements DataMapperInterface
{
    protected $_adapter;
    protected $_collection;
    protected $_entityClass;
    protected $_entityTable; 
        
    /**
     * Class constructor
     */
    public function __construct(MySQLAdapter $adapter, CollectionAbstract $collection, array $entityOptions = array())
    {
        $this->_adapter = $adapter;
        $this->_collection = $collection;
        if (isset($entityOptions['entityClass'])) {
            $this->setEntityClass($entityOptions['entityClass']);
        }
        if (isset($entityOptions['entityTable'])) {
            $this->setEntityTable($entityOptions['entityTable']);
        }
        $this->init();
    }
   
    /**
     * Initialize the data mapper here
     */
    public function init(){}
   
    /**
     * Get the instance of the database adapter
     */
    public function getAdapter()
    {
        return $this->_adapter;
    }
   
    /**
     * Get the collection the mapper uses
     */
    public function getCollection()
    {
        return $this->_collection;
    }
   
    /**
     * Set the class for reconstructing entities
     */
    public function setEntityClass($entityClass)
    {
        if (!class_exists($entityClass, false)) {
            throw new DataMapperException('The specified entity class ' . $entityClass . ' does not exist.');
        }
        $this->_entityClass = $entityClass;
    }
   
    /**
     * Get the class for reconstructing entities
     */
    public function getEntityClass()
    {
        return $this->_entityClass;
    }
   
    /**
     * Set the entity database table the mapper works with
     */
    public function setEntityTable($entityTable)
    {
        if (!is_string($entityTable) || empty($entityTable)) {
            throw new DataMapperException('The specified entity table ' . $entityTable . ' is invalid.');
        }
        $this->_entityTable = $entityTable;
    }
   
    /**
     * Get the entity database table the mapper works with
     */
    public function getEntityTable()
    {
        return $this->_entityTable;
    }
   
    /**
     * Find an entity by its ID
     */
    public function findById($id)
    {
        $id = (int) $id;
        $this->_adapter->select($this->_entityTable, "id = $id");
        if ($data = $this->_adapter->fetch()) {
            return new $this->_entityClass($data);
        }
        return null;
    }
   
    /**
     * Find all the entities
     */
    public function findAll()
    {
        $this->_collection->clear();
        $this->_adapter->select($this->_entityTable);
        while ($data = $this->_adapter->fetch()) {
            $this->_collection->add($data['id'], new $this->_entityClass($data));   
        }
        return $this->_collection->count() !== 0 ?
               $this->_collection :
               null;
    }
   
    /**
     * Find all the entities that match the specified criteria
     */
    public function search($criteria)
    {
        $this->_collection->clear();
        $this->_adapter->select($this->_entityTable, $criteria);
        while ($data = $this->_adapter->fetch()) {
            $this->_collection->add($data['id'], new $this->_entityClass($data));   
        }
        return $this->_collection->count() !== 0 ?
               $this->_collection :
               null;
    }
}

 

(DataMapperException.php)

<?php

class DataMapperException extends Exception{}

While it’s fair to admit that, at a glance, the source code of the above “DataMapperAbstract” class looks somewhat complex, this is a misleading impression. In fact, the class is a generic mapper that concentrates most of the functionality required to perform CRUD operations in a MySQL database table. It’s that simple, really.

In addition, you should notice that its constructors inject two collaborators. The first one is an instance of the previous MySQL adapter, which is utilized internally for executing the aforementioned operations. The second one is an abstract collection object, which is used within the “findAll()” and “search()” methods. You're wondering where the latter comes from, right? This object is an instance of a countable, iteratable class responsible for handling collections of entities. But leave your troubles behind, at least for the moment, and don’t feel concerned about the implementation of this class; it’ll be covered in detail in upcoming tutorials in this series.

Needless to say, if you test the “DataMapperAbstract” class in its current state, you’ll get an ugly fatal error; it doesn’t fully implement all of the methods declared by the “DataMapperInterface” interface. Again, fear not, as these pending methods will be added to the class and discussed in depth in the following installment.

It’s safe to say, however, that the mapping layer of this sample application is near completion. It now includes a generic data mapper which can be easily subclassed to create concrete mappers, including the one responsible for handling user objects. This is a remarkable advance toward the implementation of a user repository.

Final thoughts

In this third episode of the series, I added to this sample application a simple mapping layer composed of a single interface and a generic mapper class. Even though the development of the latter is still incomplete, as it’s necessary to add the methods that save and delete entities, it can already be used effectively as a mediator between the domain and data access layers defined previously.

As I said before, the implementation of these missing methods will be covered in the next tutorial, so if you’re interested in learning how this will be done, don’t miss the upcoming part!



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: