Home arrow PHP arrow Page 2 - PHP Services Layers: Data Mappers

Building an abstract data mapper - PHP

In this second part of a series on PHP services layers, I add a couple of data mappers to our sample application. These data mappers will allow us to bridge the domain model with the data access layer. With this addition, we're one step closer to implementing a fully-functional user service.

TABLE OF CONTENTS:
  1. PHP Services Layers: Data Mappers
  2. Building an abstract data mapper
By: Alejandro Gervasio
Rating: starstarstarstarstar / 0
October 17, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

To save myself from the hassle of reinventing the wheel, the data mappers (http://martinfowler.com/eaaCatalog/dataMapper.html) that I plan to use will be pretty similar to the ones shown here (http://www.devshed.com/c/a/PHP/Roll-Your-Own-Repository-in-PHP-Data-Mapper-Finishing-Touches/). They implement a segregated interface, whose contract looks like this:

(MyApplication/Mapper/DataMapperInterface.php)

<?php

namespace MyApplicationMapper;

interface DataMapperInterface
{
    public function findById($id);

    public function findAll();

    public function search($criteria);

    public function insert($entity);

    public function update($entity);

    public function delete($entity);
}

The above interface doesn’t require any further analysis, so let's move on and look at the class below. It implements an abstract data mapper:

(MyApplication/Mapper/AbstractDataMapper.php)

<?php

namespace MyApplicationMapper;
use MyApplicationDatabase,
    MyApplicationCollection;

abstract class AbstractDataMapper implements DataMapperInterface
{
    protected $_adapter;
    protected $_collection;
    protected $_entityTable;
    protected $_entityClass;

    /**
     * Constructor
     */
    public function  __construct(DatabaseDatabaseAdapterInterface $adapter, CollectionAbstractCollection $collection, array $entityOptions = array())
    {
        $this->_adapter = $adapter;
        $this->_collection = $collection;
        if (isset($entityOptions['entityTable'])) {
            $this->setEntityTable($entityOptions['entityTable']);
        }
        if (isset($entityOptions['entityClass'])) {
            $this->setEntityClass($entityOptions['entityClass']);
        }
    }

    /**
     * Get the database adapter
     */
    public function getAdapter()
    {
        return $this->_adapter;
    }

    /**
     * Get the collection
     */
    public function getCollection()
    {
        return $this->_collection;
    }

    /**
     * Set the entity table
     */
    public function setEntityTable($entityTable)
    {
        if (!is_string($table) || empty ($entityTable)) {
            throw new DataMapperException('The specified entity table is invalid.');
        }
        $this->_entityTable = $entityTable;
    }

    /**
     * Get the entity table
     */
    public function getEntityTable()
    {
        return $this->_entityTable;
    }

    /**
     * Set the entity class
     */
    public function setEntityClass($entityClass)
    {
        if (!class_exists($entityClass)) {
            throw new DataMapperException('The specified entity class is invalid.');
        }
        $this->_entityClass = $entityClass;
    }

    /**
     * Get the entity class
     */
    public function getEntityClass()
    {
        return $this->_entityClass;
    }

    /**
     * Find an entity by its ID
     */
    public function findById($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->_adapter->select($this->_entityTable);
        while ($data = $this->_adapter->fetch($this->_entityTable)) {
            $this->_collection[] = new $this->_entityClass($data);
        }
        return $this->_collection;
    }

    /**
     * Find all the entities that match the specified criteria
     */
    public function search($criteria)
    {
        $this->_adapter->select($this->_entityTable, $criteria);
        while ($data = $this->_adapter->fetch()) {
            $this->_collection[] = new $this->_entityClass($data);
        }
        return $this->_collection;
    }
   
    /**
     * Insert a new row in the table corresponding to the specified entity
     */
    public function insert($entity)
    {
        if ($entity instanceof $this->_entityClass) {
            return $this->_adapter->insert($this->_entityTable, $entity->toArray());
        }
        throw new DataMapperException('The specified entity is not allowed for this mapper.');
    }

    /**
     * Update the row in the table corresponding to the specified entity
     */
    public function update($entity)
    {
        if ($entity instanceof $this->_entityClass) {
            $data = $entity->toArray();
            $id = $entity->id;
            unset($data['id']);
            return $this->_adapter->update($this->_entityTable, $data, "id = $id");
        }
        throw new DataMapperException('The specified entity is not allowed for this mapper.');
    }

    /**
     * Delete the row in the table corresponding to the specified entity or ID
     */
    public function delete($id)
    {
        if ($id instanceof $this->_entityClass) {
            $id = $id->id;
        }
        return $this->_adapter->delete($this->_entityTable, "id = $id");
    }
}

 

(MyApplication/Mapper/DataMapperException.php)

<?php

namespace MyApplicationMapper;

class DataMapperException extends Exception{}

At the risk of being repetitive, what I said about the previous “AbstractEntity” class applies to the earlier mapper as well: despite its apparent complexity, the logic that stands behind it is fairly straightforward. In short, its functionality is reduced to performing CRUD operations on generic entities, achieved via a database adapter, which is injected in the constructor.

Of course, the lonely existence of an abstract mapper doesn’t buy us too much as far as moving user entities between the domain model and the persistence layer. To tackle this issue, it’s necessary to spawn a concrete mapper that works exclusively with user objects.

This is exactly what I’ll be doing in the upcoming segment. 

Mapping user entities: implementing a user mapper

If you ever thought that building a data mapper that works specifically with user entities was a challenging and difficult task, then take a look at the class below. It is called “UserMapper,” and its implementation is as follows: 

(MyApplication/Mapper/UserMapper.php)

<?php

namespace MyApplicationMapper;
use MyApplicationDatabase,
    MyApplicationCollection;

class UserMapper extends AbstractDataMapper
{
    protected $_entityClass = 'MyApplicationEntityUser';
    protected $_entityTable = 'users'; 
   
    /**
     * Constructor
     */
     public function __construct(DatabaseDatabaseAdapterInterface $adapter, CollectionUserCollection $collection)
     {
         parent::__construct($adapter, $collection);
     }  
}

That was really quick to code and read, wasn’t it? This user mapper simply overrides its parent’s constructor to make sure that only collections of user entities will be injected into its internals. Besides, the $_entityClass and $_entityTable properties assure that the mapper will move data between the mentioned entities and a “users” database table residing in the persistence layer. The latter will be discussed in depth in upcoming installments of the series.

With this user mapper ready to go, we’ve made significant progress toward the development of a service layer. However, there are still some pieces that need to be added to the current structure of this sample application. These include the construction of the collaborators taken by the pertinent mappers. 

Final Thoughts

In this second chapter of the series, things got a little more interesting as I added a couple of data mappers to this sample application. These data mappers allowed us to bridge its domain model with the data access layer. Even though it may sound like a marketing phrase, we’re closer to implementing a fully-functional user service. For the moment, be patient and everything will work like a charm, believe me.

On the other hand, you may have noticed that the previous data mappers accept two dependencies to work as expected: the first one is a database adapter, while the second one is an instance of a collection handling class. Well, there’s no need to be a rocket scientist to realize that these collaborators must be properly implemented as well.

In the next tutorial I’ll be showing you the full source code of the former (the latter will be covered in detail in a future installment).

Don’t miss the next 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: