Home arrow PHP arrow PHP Service Layers: Database Adapters

PHP Service Layers: Database Adapters

In this third part of a series, I will extend the functionality of the sample application by adding an easily customizable data access layer to it. Implementing this structure will be a fairly straightforward process. Its building blocks will include a simple segregated interface along with a basic MySQL adapter.

TABLE OF CONTENTS:
  1. PHP Service Layers: Database Adapters
  2. Building a simple data access layer
By: Alejandro Gervasio
Rating: starstarstarstarstar / 0
October 17, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

While the term may make you nervous, the truth is a service is nothing but an abstraction layer which permits you to interface application logic with different client layers. These client layers can range from several front-ends, to the so-called action controllers that can be found in many MVC frameworks available today.

Put in a simpler way, a service is an enterprise-level pattern, and as such, it’s both language and platform agnostic. This means that it can be implemented in most programming languages, including PHP, even though in general it’s not considered by many to be a true “enterprisey” contender. Of course, abstaining from the joys of creating a functional service layer only because there exist some biased opinions is a lame excuse. Therefore, in previous chapters of this series I started building a basic web application, whose functionality is aimed at performing CRUD operation on some user entities through a basic service.

Admittedly, in its current state the application doesn’t do anything especially useful. So far, I've only implemented its domain and mapping layers. However, this is about to change. In this article I'll be adding to it a whole new layer, tasked with persisting domain objects in the underlying storage mechanism -- in this case, a MySQL database, but it could be a web service, etc. As you’ll see in a moment, this data access layer will be made up of a segregated interface and a basic MySQL abstraction class, which you’ll be able to customize at will.

To sum up: are you ready to continue learning how to progressively implement a user service layer in PHP? Then let's get started.

Interfacing domain objects with the persistence layer: a quick look at the previous data mappers

As usual, before I start developing the persistence layer corresponding to the sample application mentioned in the introduction, I’d like to spend a few moments reviewing the topics covered in the preceding installment of the series. In that tutorial I implemented the application’s mapping layer, which was comprised of a simple segregated interface and a couple of data mappers.

Well, here’s the definition of the pertinent interface, which makes extending the mapping layer a breeze:

(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 contract defined by the above interface speaks for itself. So let's move on and take a look at the following abstract class, which encapsulates the functionality required to map generic entities:

(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{}

From the above code fragment, it’s clear to see that all the “AbstractMapper” class does is fetch, save and remove entities from the storage layer. While this is all well and good, my purpose here is to implement a functional user service. Therefore, it’s mandatory to spawn a subclass from the abstract parent that works exclusively with user entities. The one shown below does exactly that:

(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 much easier to accomplish than one might think, wasn’t it? In a few easy steps, I managed to implement an extendable mapping layer, which moves data between the domain model and the underlying database.

However, there’s a couple of things that need to be done to persist the model in the storage mechanism. First, we need to build the database adapter injected in the mappers’ constructor; and second, we need to develop the other dependency, namely a collection handling class.

But first things first, right? Thus, in the following section I’ll be defining the contract implemented by the former, which logically will be represented by an interface.

To learn more about this process, click on the link below and keep reading.



 
 
>>> 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: