Home arrow PHP arrow Building a Unit of Work in PHP: Creating a Data Mapper and Handling Entities

Building a Unit of Work in PHP: Creating a Data Mapper and Handling Entities

In this second tutorial of the series, I add to the previous UoW class a pair of collaborators that it needs to function properly. These are an abstract data mapper tasked with interacting with the persistence layer, and an additional abstract class responsible for modeling generic entities.

TABLE OF CONTENTS:
  1. Building a Unit of Work in PHP: Creating a Data Mapper and Handling Entities
  2. Adding a collaborator to the UoW: defining a simple abstract data mapper
By: Alejandro Gervasio
Rating: starstarstarstarstar / 3
December 27, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

A Unit of Work (UoW) is an enterprise-level design pattern that permits you to apply a transactional model to the domain objects that integrate an application, to synchronize and optimize the operations performed on them. Even though this definition may sound hard to grasp, and like it concerns the most esoteric facets of the OOP paradigm, the truth is that understanding the logic that drives this pattern is a fairly straightforward process, especially when using an easy-going language like PHP.

Of course, highlighting the benefits of a UoW from a theoretical standpoint would be pretty pointless if the process isn’t backed up with some functional code samples. In the introductory installment of this series, I went through the development of an abstract UoW class. According to the model imposed by the pattern, it was provided with the ability to “mark” entities for further insertion, update and removal via a set of discrete methods. Lastly, a call to the class’s “commit()” method allowed it to perform all these queued operation in the persistence layer in just one go.

Although the current implementation of this sample UoW class is clear enough to grasp very quickly, it’s valid to point out that the class takes a couple of collaborators that make it work as expected. The first one is a data mapper, which in turn executes the queued operations in the underlying persistence mechanism, while the second one is an instance of a class responsible for modeling generic entities. For obvious reasons, before you see the UoW up and running, it’s necessary to define the originating classes of these dependencies.

Given that, in this second chapter of the series I’m going to show you these brand new classes, so you can see how they fit in the schema dictated by the UoW. Now, jump forward and start reading!

Review: performing domain objects operations in a transactional manner

As usual, before I create the classes that model the two dependencies just mentioned, it’d be useful to recall the definition of the abstract UoW built in the preceding chapter. This sample UoW is capable, among other things, of setting entities for further insertion, updating and deletion. Its full source code is shown below. Pay close attention to it, please:

(UnitOfWorkAbstract.php)

<?php

abstract class UnitOfWorkAbstract
{
    protected $_newEntities = array();
    protected $_cleanEntities = array();
    protected $_dirtyEntities = array();
    protected $_removedEntities = array();
    protected $_dataMapper;
   
    /**
     * Class constructor
     */
    public function __construct(DataMapperAbstract $dataMapper)
    {
        $this->_dataMapper = $dataMapper;
    }
   
    /**
     * Get the data mapper the Unit of Work uses
     */         
    public function getDataMapper()
    {
        return $this->_dataMapper;
    }
   
    /**
     * Mark an entity 'new'
     */
    public function markNew(EntityAbstract $entity)
    {
        $id = $entity->id;
        if ($id !== null) {
            if (array_key_exists($id, $this->_cleanEntities)) {
                unset($this->_cleanEntities[$id]);
            }
            if (array_key_exists($id, $this->_dirtyEntities)) {
                unset($this->_dirtyEntities[$id]);
            }
            if (array_key_exists($id, $this->_removedEntities)) {
                unset($this->_removedEntities[$id]);
            }
        }
        if (!in_array($entity, $this->_newEntities, true)) {
            $this->_newEntities[] = $entity;
        }
    }
   
    /**
     * Remove an entity previously marked 'new'
     */
    protected function _removeNew(EntityAbstract $entity)
    {
        if (in_array($entity, $this->_newEntities, true)) {
            $newEntities = array();
            foreach ($this->_newEntities as $_newEntity) {
                if ($entity !== $_newEntity) {
                    $newEntities[] = $_newEntity;
                }
            }
            $this->_newEntities = $newEntities;
        }
    }
   
    /**
     * Get all the 'new' entities
     */
    public function getNewEntities()
    {
        return $this->_newEntities;
    }
   
    /**
     * Mark an entity 'clean'
     */
    public function markClean(EntityAbstract $entity)
    {
        $this->_removeNew($entity);
        $id = $entity->id;
        if ($id !== null) {
            if (array_key_exists($id, $this->_dirtyEntities)) {
                unset($this->_dirtyEntities[$id]);
            }
            if (array_key_exists($id, $this->_removedEntities)) {
                unset($this->_removedEntities[$id]);
            }
            if (!array_key_exists($id, $this->_cleanEntities)) {
                $this->_cleanEntities[$id] = $entity;
            }
        }
    }
   
    /**
     * Get all the 'clean' entities
     */
    public function getCleanEntities()
    {
        return $this->_cleanEntities;
    }
   
    /**
     * Mark an entity 'dirty'
     */
    public function markDirty(EntityAbstract $entity)
    {
        $this->_removeNew($entity);
        $id = $entity->id;
        if ($id !== null) {
            if (array_key_exists($id, $this->_cleanEntities)) {
                unset($this->_cleanEntities[$id]);
            }
            if (array_key_exists($id, $this->_removedEntities)) {
                unset($this->_removedEntities[$id]);
            }
            if (!array_key_exists($id, $this->_dirtyEntities)) {
                $this->_dirtyEntities[$id] = $entity;
            }
        }
    }
   
    /**
     * Get all the 'dirty' entities
     */
    public function getDirtyEntities()
    {
        return $this->_dirtyEntities;
    }
   
    /**
     * Mark an entity 'removed'
     */
    public function markRemoved(EntityAbstract $entity)
    {
        $this->_removeNew($entity);
        $id = $entity->id;
        if ($id !== null) {
            if (array_key_exists($id, $this->_cleanEntities)) {
                unset($this->_cleanEntities[$id]);
            }
            if (array_key_exists($id, $this->_dirtyEntities)) {
                unset($this->_dirtyEntities[$id]);
            }
            if (!array_key_exists($id, $this->_removedEntities)) {
                $this->_removedEntities[$id] = $entity; 
            }
        }
    }
   
    /**
     * Get all the 'removed' entities
     */
    public function getRemovedEntities()
    {
        return $this->_removedEntities;
    }
   
    /**
     * Clear all the 'new' entities
     */ 
    public function clearNew()
    {
        $this->_newEntities = array();
        return $this;
    }
   
    /**
     * Clear all the 'clean' entities
     */
    public function clearClean()
    {
        $this->_cleanEntities = array();
        return $this;
    }
   
    /**
     * Clear all the 'dirty' entities
     */ 
    public function clearDirty()
    {
        $this->_dirtyEntities = array();
        return $this;
    }
   
    /**
     * Clear all the 'removed' entities
     */ 
    public function clearRemoved()
    {
        $this->_removedEntities = array();
        return $this;
    }
   
    /**
     * Clear all the entities stored in the Unit Of Work
     */
    public function clearAll()
    {
        $this->clearNew()
             ->clearClean()
             ->clearDirty()
             ->clearRemoved();
    }
   
    /**
     * Find an entity by its ID (implements an identity map)
     */
    public function findById($id)
    {
        if (array_key_exists($id, $this->_cleanEntities)) {
            return $this->_cleanEntities[$id];
        }
        if ($entity = $this->_dataMapper->findById($id)) {
            $this->markClean($entity);
            return $entity;
        }
        return null;  
    }
   
    /**
     * Find all the entities
     */
    public function findAll()
    {
        $collection = $this->_dataMapper->findAll();
        if ($collection !== null) {
            foreach ($collection as $entity) {
                $this->markClean($entity);
            }
            return $collection;
        }
        return null;
    }
   
    /**
     * Commit all the pending entity operations in one go (insert, update, delete)
     */
    public function commit()
    {  
        // save all the 'new' entities
        if (!empty($this->_newEntities)) {
            foreach ($this->_newEntities as $_newEntity) {
                $this->_dataMapper->insert($_newEntity);           
            }
        }
        // update all the 'dirty' entities
        if (!empty($this->_dirtyEntities)) {
            foreach ($this->_dirtyEntities as $_dirtyEntity){
                $this->_dataMapper->update($_dirtyEntity);
            }
        }
        // delete all the 'removed' 'entities
        if (!empty($this->_removedEntities)) {
            foreach ($this->_removedEntities as $_removedEntity) {
                $this->_dataMapper->delete($_removedEntity);
            }
        }
    }  
}

From the above code sample, it’s clear to see that the actual workhorses of the UoW are the “markNew(),” “markClean(),” “markDirty()” and “markRemoved()” methods. Along with the aforementioned “commit()” method, allow you to queue entities in memory and execute the pending operations in one go.

Even though the implementation of each method seems to be complex, at least at a glance, if you analyze them more closely you’ll realize that they do their business by handling only plain array elements. Of course, while it’s fairly easy to understand how the abstract UoW does its thing, at this point there’s not much that can be done with it, because we need to create the dependencies that it uses internally.

To fix this issue, in the following section I’m going to show you the definition of the abstract data mapper injected through the UoW’s constructor. Therefore, to learn more on this topic, go ahead and read the lines to come.



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