Home arrow PHP arrow Page 2 - Building a Unit of Work in PHP: Applying a Transactional Model to Domain Objects

Making the Unit of Work more functional: clearing entities and performing entity transactions - PHP

Are you trying to optimize the way your web application handles domain objects, and not finding a good solution? If caching won't help, and data and identity mappers won't suit your needs, you might want to look at using a Unit of Work pattern. What is a UoW? Keep reading to learn how it can help you.

TABLE OF CONTENTS:
  1. Building a Unit of Work in PHP: Applying a Transactional Model to Domain Objects
  2. Making the Unit of Work more functional: clearing entities and performing entity transactions
By: Alejandro Gervasio
Rating: starstarstarstarstar / 3
December 21, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

To leverage the full potential of the UoW just created, it's necessary to provide it with the ability to insert, update and remove previously-marked entities through a single method call. Since these objects are stored in protected arrays, achieving this is much simpler than you might think.

But if you're still skeptical, look at the code fragment below. It shows the definition of this brand new method, together with a few additional ones: 

/**
 * 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 (create, 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);
        }
    }
}

Definitely, things are becoming much more interesting! As you can see above, now the UoW not only implements some convenience methods that allow you to selectively clear previously-marked entities, but it also defines a couple of typical finders, such as "findById()" and "findAll()." Although it's worthwhile to analyze these extra methods in detail, you should pay attention to the real workhorse here, which is obviously "commit()."

The existence of this method justifies the development of the entire class, since its implementation gives a real meaning to the transactional nature of a UoW. In this case, the method iterates over each set of marked entities and performs the corresponding operation (insert, update or delete) through the injected mapper. This shows how convenient it can be to use a UoW to reduce the accesses to the persistence layer to a bare minimum. Not too bad, huh?

Since you've grasped how these methods do their thing, it's time to show the UoW's full source code. This will be done below, so just keep reading.  

The Unit of Work's full source code

As I promised above, below I included the finished version of the abstract UoW, which will make it easier for you to understand the role played by each method that it defines. Check it out:

(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 (create, 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);
            }
        }
    }  
}

The work is done, at least for the moment. At this stage you should have a more accurate idea of what a Unit of Work is and how it can be used for optimizing transactions with domain objects.

But this is only the beginning of the journey. I plan to show you how to use this sample UoW in a pretty realistic scenario. This will done progressively in upcoming tutorials.  

Wrapping up

In this first episode of the series, I provide you with a humble introduction to what a Unit of Work is and how to implement it in a step-by-step fashion in PHP. While a lot of material has been covered so far, there's still a long road ahead of us. The base UoW just created takes a few additional collaborators, namely an abstract mapper and the corresponding entities, whose originating classes haven't been defined yet.

In the next tutorial I'm going to show the source code of these classes, so you can see more clearly how they can be put to work side by side with the earlier UoW.

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: