PHP Proxy Patterns: Collections and Classes

In this second part of a three-part tutorial, I add to the existing structure of our sample blog program a host of useful components. These include the classes that handle collections of entities and views, and most importantly, the ones responsible for lazy-loading domain objects from the database.

Although they still haven’t gained the huge level of popularity of other design patterns (at least in the PHP field), proxies are a powerful paradigm that allows users to perform some clever tasks in OOP. These include unit testing classes, introspecting other objects, and even implementing more efficient data persistence strategies (pretty similar to what Doctrine 2.x does behind the scenes with entities).

Of course, talking about the virtues that proxy objects offer without backing up the words with code samples is pointless. Therefore, in the introductory part of this tutorial, I started developing an extendable blog application. It took advantage of the functionality of a proxy class to lazy-load, from a MySQL database, the comments associated to a given blog entry.

Well, to be frank, so far I’ve demonstrated how an instance of the proxy class can be used to fill the “comments” field of each entry. If you’re anything like me, however, you want to see the actual implementation of this class, along with a few others, which comprise the blog. Fear not; in this second installment, I’ll be building all of these remaining classes, so that you can see more clearly how they fit into the blog’s existing structure.

Ready to learn how the use of proxy objects can optimize the persistence of a domain model? Then don’t hesitate anymore; read the lines to come!

Adding more functionality to the blog: handling collections of entities

As I noted in the introduction, it’s necessary to build some additional classes (including the proxies) to make the blog program entirely functional. The first class I plan to show you handles collections of entities, which allows for easy iteration over multiple entries, as well as over the comments related to a particular blog entry.   

Below I listed for you the interface implemented by this class, followed by the class itself. Check them out:

(MyApp/Common/CollectionInterface.php)

<?php

namespace MyAppCommon;
use MyAppModel;

interface CollectionInterface extends Countable, IteratorAggregate, ArrayAccess
{
    public function toArray();

    public function clear();

    public function reset();

    public function add($key, ModelAbstractEntity $entity);
   
    public function get($key);

    public function remove($key);

    public function exists($key);
}

 

(MyApp/Common/EntityCollection.php)

<?php

namespace MyAppCommon;
use MyAppModel;

class EntityCollection implements CollectionInterface
{
    protected $_entities = array();

    /**
     * Constructor
     */
    public function __construct(array $entities = array())
    {
        $this->_entities = $entities;
        $this->reset();
    }

    /**
     * Get the entities stored in the collection
     */
    public function toArray()
    {
        return $this->_entities;
    }

    /**
     * Clear the collection
     */
    public function clear()
    {
        $this->_entities = array();
    }
    
    /**
     * Rewind the collection
     */    
    public function reset()
    {
        reset($this->_entities);
    }

    /**
     * Add an entity to the collection
     */
    public function add($key, ModelAbstractEntity $entity) {
        return $this->offsetSet($key, $entity);
    }

    /**
     * Get from the collection the entity with the specified key
     */
    public function get($key)
    {
        return $this->offsetGet($key);
    }

    /**
     * Remove from the collection the entity with the specified key
     */
    public function remove($key)
    {
        return $this->offsetUnset($key);
    }

    /**
     * Check if the entity with the specfied key exists in the collection
     */
    public function exists($key)
    {
        return $this->offsetExists($key);
    }

    /**
     * Count the number of entities in the collection
     */
    public function count()
    {
        return count($this->_entities);
    }

    /**
     * Get the external array iterator
     */
    public function getIterator()
    {
        return new ArrayIterator($this->toArray());
    }

    /**
     * Add an entity to the collection
     */
    public function offsetSet($key, $entity)
    {
        if (!$entity instanceof ModelAbstractEntity) {
            throw new InvalidArgumentException(‘To add an entity to the collection, it must be an instance of EntityAbstract.’);
        }
        if (!isset($key)) {
            $this->_entities[] = $entity;
        }
        else {
            $this->_entities[$key] = $entity;
        }
        return true;
    }
   
    /**
     * Remove an entity from the collection
     */
    public function offsetUnset($key)
    {
        if ($key instanceof ModelAbstractEntity) {
            $this->_entities = array_filter($this->_entities, function ($v) use ($key) {
                return $v !== $key;
            });
            return true;
        }
        if (isset($this->_entities[$key])) {
            unset($this->_entities[$key]);
            return true;
        }
        return false;
    }
   
    /**
     * Get the specified entity in the collection
     */
    public function offsetGet($key)
    {
        return isset($this->_entities[$key])
            ? $this->_entities[$key] : null;
    } 
   
    /**
     * Check if the specified entity exists in the collection
     */    
    public function offsetExists($key)
    {
        return isset($this->_entities[$key]);
    }
}

Leaving aside the contract defined by the “CollectionInterface” interface, which is easy to grasp, you should focus your attention on its implementer “EntityCollection.” As you can see, this class is nothing but an iteratable and countable array collection. It allows you to manipulate groups of entities in a straightforward fashion. Its driving logic is as simple as that.    

In a typical use case, this class should be utilized for traversing the comments related to a specific blog entry, or even for processing its related comments. However, this would imply that each time that we fetched an entry from the database, we would have to fetch the comments, too. In short, we might end up pulling out the entire database… yes, unnecessarily.

As I noted at the start, this issue can be addressed by using a proxy that acts like a mediator for the actual entity collection, which internally knows how to fetch the comments from the storage on request. Thus, in the next section I’ll be showing you the definition of this proxy class, so that you can understand how it works.  

Now, click on the link below and keep reading.

{mospagebreak title=Building a couple of proxy classes}

As with many other aspects of OOP, there are several ways to create proxies that lazy-load data from the persistence layer. In this case, though, I’m going to following an approach similar to the one taken by Ben Scholzen, which uses a specific mapper to get a collection of domain objects on request.

On the other hand, my own take is a bit more extendable. It makes the proxy class an implementer of the following interface:  

(MyApp/Common/LoadableInterface.php)

<?php

namespace MyAppCommon;

interface LoadableInterface
{
    public function load(); 
}

Having such a generic contract at our disposal, it’s really simple to create different proxy classes that lazy-load data from storage, as long as they implement the “LoadableInterface.” But, since I want to enable the blog to lazy-load single entities and collections, the next step is to define an abstract proxy that encapsulates common functionality for both use cases.

The following class does precisely that:  

(MyApp/Common/AbstractProxy.php)

<?php

namespace MyAppCommon;
use MyAppModelMapper;

abstract class AbstractProxy
{
    protected $_mapper;
    protected $_params;
   
    /**
     * Constructor
     */
    public function __construct(MapperAbstractMapper $mapper, $params)
    {
        if (!is_string($params) || empty($params)) {
            throw new InvalidArgumentException(‘The mapper parameters are invalid.’);
        }
        $this->_mapper = $mapper;
        $this->_params = $params; 
    }      
}

Given that this abstract proxy injects a mapper into the constructor, along with the corresponding arguments, building a concrete proxy that lazy-loads a single entity from the persistence layer is this simple:

(MyApp/Common/EntityProxy.php)

<?php

namespace MyAppCommon;
use MyAppModel;

class EntityProxy extends AbstractProxy implements LoadableInterface
{
    protected $_entity;
   
    /**
     * Load an entity via the ‘findById()’ method of the injected mapper
     */
    public function load()
    {
        if ($this->_entity === null) {
            $this->_entity = $this->_mapper->findById($this->_params);
            if (!$this->_entity instanceof ModelAbstractEntity) {
                throw new RunTimeException(‘Unable to load the specified entity.’);
            }
        }
        return $this->_entity;
    }  
}

As you can see, the “EntityProxy” class uses the “findById()” method of the injected mapper (remember that one?) to fetch an entity from the database. Of course, this process is executed whenever its “load()” method is invoked, which allows it to easily fetch a single domain object on request. Pretty good, isn’t it?

What’s more, if you found it easy to understand how the earlier proxy class does its thing, be sure to check the following one. It performs a similar task with a collection of entities:

(MyApp/Common/CollectionProxy.php)

<?php

namespace MyAppCommon;

class CollectionProxy extends AbstractProxy implements LoadableInterface, Countable, IteratorAggregate
{
    protected $_collection;
   
    /**
     * Load explicitly a collection of entities via the ‘find()’ method of the injected mapper
     */
    public function load()
    {
        if ($this->_collection === null) {
            $this->_collection = $this->_mapper->find($this->_params);
            if (!$this->_collection instanceof EntityCollection) {
                throw new RunTimeException(‘Unable to load the specified collection.’);
            }
        }
        return $this->_collection;
    }
   
    /**
     * Count the entities in the collection after lazy-loading them
     */
    public function count()
    {
        return count($this->load());
    } 
   
    /**
     * Load a collection of entities via the ‘find()’ method of the injected mapper
     * when called within a ‘foreach’ construct
     */
    public function getIterator()
    {
        return $this->load();
    }   
}

There you have it. As you can see, the above “CollectionProxy” class uses the “find()” method of the injected mapper to lazy-load a collection of domain objects from storage. This process can be explicitly triggered either by calling the class’s “load()” method, or transparently when an instance of the class is used within a “foreach” construct (or when counting the elements stored in the collection).

So far, so good. At this point, you should have a clearer idea of how to the implementation of a couple of proxy classes can help improve the way that a domain model is persisted. But, before you see these classes into action, it’s necessary to add other components to the blog, including a module that handles views.

This is exactly what I’ll be doing in the following section.

Defining the blog’s landing page: building a view class

Since I want to keep the blog’s source code clear and easy to follow, the layer responsible for manipulating views will be simple. It will be composed of only a single class and an HTML template containing interspersed PHP code.

If you want to see this view handling class, check it  below:

(MyApp/Views/View.php)

<?php

namespace MyAppViews;

class View
{
    protected $_values = array();
    protected $_templateDirectory = ‘Templates’;
    protected $_templateFile = ‘default_template.php’;

    /**
     * Constructor
     */
    public function __construct(array $templateOptions = array())
    {
        // Set the view template directory
        if (isset($templateOptions['templateDirectory'])) {
            $this->setTemplateDirectory($templateOptions['templateDirectory']);
        }
        // Set the view template file
        if (isset($templateOptions['templateFile'])) {
            $this->setTemplateFile($templateOptions['templateFile']);
        }
    }

    /**
     * Set the view template directory
     */
    public function setTemplateDirectory($templateDirectory)
    {
        if (!is_dir($templateDirectory)) {
            throw new InvalidArgumentException("The template directory ‘$templateDirectory’ is invalid.");
        }
        $this->_templateDirectory = $templateDirectory;   
    }

    /**
     * Get the template directory
     */
    public function getTemplateDirectory()
    {
        return $this->_templateDirectory;
    }
     
    /**
     * Set the view template file
     */
    public function setTemplateFile($templateFile)
    {
        $templateFile = $this->_templateDirectory . DIRECTORY_SEPARATOR . $templateFile;
        if (!file_exists($templateFile) || !is_readable($templateFile)) {
            throw new InvalidArgumentException("The template file ‘$templateFile’ is invalid.");
        }
        $this->_templateFile = $templateFile;
    }
     
    /**
     * Get the view template file
     */
    public function getTemplateFile()
    {
        return $this->_templateFile;
    }

    /**
     * Assign a value to the specified field of the view via the corresponding mutator (if it exists);
     * otherwise, assign the value directly to the ‘$_values’ protected array
     */
    public function __set($name, $value)
    {
        $mutator = ‘set’ . ucfirst(strtolower($name));
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);
        }
        else {
            $this->_values[$name] = $value;
        }
    }

    /**
     * Get the value assigned to the specified field of the view via the corresponding getter (if it exists);
     * otherwise, get the value directly from the ‘$_values’ protected array
     */
    public function __get($name)
    {
        $accessor = ‘get’ . ucfirst(strtolower($name));
        if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
            return $this->$accessor;
        }
        if (isset($this->_values[$name])) {
            return $this->_values[$name];
        }
        throw new InvalidArgumentException("The field ‘$name’ has not been set for this view yet.");
    }

    /**
     * Check if the specified field has been assigned to the view
     */
    public function __isset($name)
    {
        return isset($this->_values[$name]);
    }

    /**
     * Unset the specified field from the view
     */
    public function __unset($name)
    {
        if (isset($this->_values[$name])) {
            unset($this->_values[$name]);
            return true;
        }
        throw new InvalidArgumentException("The field ‘$name’ has not been set for this view yet.");
    }

    /**
     * Render the template
     */
    public function render()
    {
        extract($this->_values);
        ob_start();
        include $this->_templateFile;
        return ob_get_clean();
    }

    /**
     * Get an associative array with the values assigned to the fields of the view
     */
    public function toArray()
    {
        return $this->_values;
    }
}

Even though the definition of this view class is somewhat lengthy, its driving logic can be grasped quickly. Simply put, the class behaves like a data holder that assigns, removes and gets fields from spawned view objects, which are in turn rendered inside a specified template file. It’s that simple. 

Since my purpose here is to show how to use the earlier proxy classes to lazy-load the comments corresponding to a particular blog entry (insertions, updates and deletions will be omitted for the sake of brevity), this is the template file that I’ll be using later on to accomplish this:

(MyApp/Views/Templates/entries.php)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Using the proxy pattern in PHP</title>
<style type="text/css">
body {
    padding: 0;
    margin: 0;
    background: #fff;
    font: 0.8em Arial, Helvetica, sans-serif;
    color: #585858;
}
h1 {
    font-size: 3em;
    color: #0080ff;
}
h2 {
    font-size: 1.8em;
    color: #E87400;
    margin: 0 0 10px 0;
}
h3 {
    font-size: 1.2em;
    color: #585858;
    margin: 0 0 15px 0;
}
p {
    margin: 0 0 15px 0;
    line-height: 18px;
}
#wrapper {
    width: 800px;
    margin: 0 auto;
}
.entry {
    padding: 20px;
    margin: 40px 0 20px 0;
    background: #f8f8f8;
    border: 1px solid #ccc;
}
.comment {
    padding: 20px;
    margin: 0 0 20px 80px;
    background: #eee;
    border: 1px solid #ccc;
}
</style>
</head>
<body>
<div id="wrapper">
    <h1><?php echo $header;?></h1>
    <?php foreach ($entries as $entry):?>
        <div class="entry">
            <h2><?php echo $entry->title;?></h2>
            <h3>Posted by <?php echo $entry->author;?></h3>
            <p><?php echo $entry->content;?></p>
        </div>
        <?php foreach ($entry->comments as $comment):?>
           <div class="comment">
                <h3><?php echo $comment->author;?> said:</h3>
                <p><?php echo $comment->content;?></p>
            </div>
        <?php endforeach;?>
    <?php endforeach;?>
</div>
</body>
</html>

Although the above template file is pretty trivial – it only contains some CSS along with a few chunks of PHP code — it shows how blog entries will be displayed on the browser, and most importantly, how the associated comments will be transparently pulled from the database on request.

Moreover, if you go back and check the implementation of the collection proxy class, you’ll recall that it fetches the pertinent comments when its “load()” method is called within a “foreach” loop (due to the implementation of the “IteratorAggregate” SPL interface). Well, that’s exactly what the template does!

With that said, are you starting to see a bit more clearly how all of the pieces that compose the blog’s structure will fit together? I’m sure you do. Nevertheless, there are still some additional components that need to be constructed first, to get a fully-functional version of the blog. These will be implemented in the last tutorial.      

Final Thoughts

In this penultimate chapter of the tutorial, I added to the existing structure of this sample blog program a host of useful components, including the classes that handle collections of entities and views, and most importantly, the ones responsible for lazy-loading domain objects from the database.

Despite of all these goodies, we haven’t finished developing the program yet. It’s necessary to encapsulate the interaction of mappers, collections and proxies behind a simpler and uncluttered API, which can easily be consumed by different front-ends. Not surprisingly, this additional API will be a service layer. In the final installment I’ll be showing you how to implement it in a few easy steps.

Don’t miss the last part!

Google+ Comments

Google+ Comments