rulururu

post Data Mapper Pattern in PHP

December 17th, 2008

Filed under: Code, PHP, Zend Framework — Brenton Alker @ 08:58

I have been trying to get together a post on the Data Mapper pattern since I started experimenting with it in a personal project. It seems to me to be a fantastic answer to the decoupling of in-memory data objects and the data store. I still don’t have all the answers, but Rob Allen’s recent post On models in a Zend Framework application, and the associated discussion provoked me to publish some of what I do have.

Warning: This post will be quite long and is very code heavy, but I hope it illustrates some of my current thoughts.

Anyway, lets get down to it.

I will be showing 4 classes. Two (2) Abstract “library” classes, and 2 examples of their use;

And now, the code. Instead of breaking the code into little pieces, I’ll add inline comments to explain what’s going on.

Classes

Library Classes

MapperAbstract

MapperAbstract is the base class for mappers. It defines a standard interface for all mappers.


abstract class MapperAbstract
{
    /**
     * Create a new instance of the DomainObject that this
     * mapper is responsible for. Optionally populating it
     * from a data array.
     *
     * @param array $data
     * @return DomainObjectAbstract
     */
    public function create(array $data = null) {
        $obj = $this->_create();
        if ($data) {
            $obj = $this->populate($obj, $data);
        }
        return $obj;
    }

    /**
     * Save the DomainObject
     *
     * Store the DomainObject in persistent storage. Either insert
     * or update the store as required.
     *
     * @param DomainObjectAbstract $obj
     */
    public function save(DomainObjectAbstract $obj)
    {
        if (is_null($obj->getId())) {
            $this->_insert($obj);
        } else {
            $this->_update($obj);
        }
    }

    /**
     * Delete the DomainObject
     *
     * Delete the DomainObject from persistent storage.
     *
     * @param DomainObjectAbstract $obj
     */
    public function delete(DomainObjectAbstract $obj)
    {
        $this->_delete($obj);
    }

    /**
     * Populate the DomainObject with the values
     * from the data array.
     *
     * To be implemented by the concrete mapper class
     *
     * @param DomainObjectAbstract $obj
     * @param array $data
     * @return DomainObjectAbstract
     */
    abstract public function populate(DomainObjectAbstract $obj, array $data);

    /**
     * Create a new instance of a DomainObject
     *
     * @return DomainObjectAbstract
     */
    abstract protected function _create();

    /**
     * Insert the DomainObject to persistent storage
     *
     * @param DomainObjectAbstract $obj
     */
    abstract protected function _insert(DomainObjectAbstract $obj);

    /**
     * Update the DomainObject in persistent storage
     *
     * @param DomainObjectAbstract $obj
     */
    abstract protected function _update(DomainObjectAbstract $obj);

    /**
     * Delete the DomainObject from peristent Storage
     *
     * @param DomainObjectAbstract $obj
     */
    abstract protected function _delete(DomainObjectAbstract $obj);
}

DomainObjectAbstract

A basic class describing an object that is part of the Domain Model. This model assumes that all DomainObjects have an integer as the primary key, but it would be a trivial change to make it a GUID for example. It also ensures the ID is immutable.


abstract class DomainObjectAbstract
{
    protected $_id = null;

    /**
     * Get the ID of this object (unique to the
     * object type)
     *
     * @return int
     */
    public function getId()
    {
        return $this->_id;
    }

    /**
     * Set the id for this object.
     *
     * @param int $id
     * @return int
     * @throws Exception	If the id on the object is already set
     */
    public function setId($id)
    {
        if (!is_null($this->_id)) {
            throw new Exception('ID is immutable');
        }
        return $this->_id = $id;
    }
}

Example Implementation Classes

User

A concrete DomainObject describing a user. This is a very barebones example, usually getters/setters would be used to set protected properties.


class User extends DomainObjectAbstract
{
    public $firstname;
    public $lastname;
    public $username;

    /**
     * Get the full name of the User
     *
     * Demonstrates how other functions can be
     * added to the DomainObject
     *
     * @return string
     */
    public function getName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}

UserMapper

A concrete implementation of a mapper between the User domain object and persistent storage.


class UserMapper extends MapperAbstract
{
    /**
     * Fetch a user object by ID
     *
     * An example skeleton of a "Fetch" function showing
     * how the database data ($dataFromDb) is used to
     * create a new User instance via the create function.
     *
     * @param string $id
     * @return User
     */
    public function findById($id)
    {
        // Query database for User with $id
        // ...
        $dataFromDb = array(
                'id'        => $id,
                'firstname' => 'Brenton',
                'lastname'  => '',
                'username'  => 'Tekerson',
            );
        return $this->create($dataFromDb);
    }

    /**
     * Poplate the User (DomainObject) with
     * the data array.
     *
     * This is a very simple example, but the mapping
     * can be as complex as required.
     *
     * @param DomainObjectAbstract $obj
     * @param array $data
     * @return User
     */
    public function populate(DomainObjectAbstract $obj, array $data)
    {
        $obj->setId($data['id']);
        $obj->firstname = $data['firstname'];
        $obj->lastname  = $data['lastname'];
        $obj->username  = $data['username'];
        return $obj;
    }

    /**
     * Create a new User DomainObject
     *
     * @return User
     */
    protected function _create()
    {
        return new User();
    }

    /**
     * Insert the DomainObject in persistent storage
     *
     * This may include connecting to the database
     * and running an insert statement.
     *
     * @param DomainObjectAbstract $obj
     */
    protected function _insert(DomainObjectAbstract $obj)
    {
        // ...
    }

    /**
     * Update the DomainObject in persistent storage
     *
     * This may include connecting to the database
     * and running an update statement.
     *
     * @param DomainObjectAbstract $obj
     */
    protected function _update(DomainObjectAbstract $obj)
    {
        // ...
    }

    /**
     * Delete the DomainObject from persistent storage
     *
     * This may include connecting to the database
     * and running a delete statement.
     *
     * @param DomainObjectAbstract $obj
     */
    protected function _delete(DomainObjectAbstract $obj)
    {
        // ...
    }
}

Example

Ok, I realize that I’ve dumped a lot of code and we’re nearly finished. Now we finally get to see how it all fits together to do something. Here, I am creating a simple example where I fetch a User based on their ID, change the Users name, and store it back to the database.


// Initialise the Mapper.
$userMapper = new UserMapper();

// Fetch and manipulate the User object
$user = $userMapper->findById(1);
$user->lastname = 'Alker';

// Tell the UserMapper that the User needs to be saved.
$userMapper->save($user);

Well, I hope everything makes sense. This is a very simplified version of the concepts I have been working on.

These simple examples could be expanded in may directions. Such as;

Validation of the DomainObject fields, I haven’t decided if the validation should be contained in the DomainObject or the Mapper.

An identity map to ensure only 1 instance of each DomainObject exists in memory at a time.

A UnitOfWork to track which objects need saving, deleting and to co-ordinate roll backs on error, or the order of persistence to satisfy foreign key constraints.

The Mapper could access many data stores; RDBMSs, Session/Cookie data or web services to name a few. Remember, the Mapper decouples the DomainObject completely from the persistent storage

Anyway, I hope this provides a fairly simple concrete example that you can build upon to create a more complete solution that you can then share with the rest of us.

post Updated Extension to Zend Framework’s ViewRenderer to Add Layouts

September 19th, 2007

Filed under: Code, PHP, Zend Framework — Brenton Alker @ 13:33

A few months back, when I was first starting with the Zend Framework, I posted my extension to the ViewRenderer helper. This is the helper that the Controller uses to work with the View. My extension was in order to implement site-wide and controller-wide layouts, allowing a consistent look and feel in an application.

I have been fairly happily using this extension since then, more recently though, I got back to fixing some problems with it. One problem I noted (and updated the previous post to reflect), was it’s double calling of the action. After further investigation, it turns out it wasn’t calling it twice, but had an image with an empty src tag in the layout, causing the browser to request the page twice.

However, It still gave me an excuse to have another look at it, and fix up some other issues. So here I am posting the revised code. If you are using the old one, it isn’t backwards compatible, but there isn’t much to change and I think it’s a much nicer implementation. It uses a stack to render the layouts in order, and is therefore much more extensible to multi-level layouts.

class Custom_Controller_Action_Helper_ViewRenderer extends Zend_Controller_Action_Helper_ViewRenderer
{
    /**
     * Defaults stack of layout scripts, listed with the innermost
     * layout first.
     *
     * @var array
     */
    protected $_defaultStack = array(
        ':controller/layout',
        'layout',
    );

    /**
     * The stack of layout scripts, listed with the innermost first.
     *
     * @var array
     */
    protected $_stack = null;

    /**
     * If the layouts should be rendered or not
     *
     * @var boolean
     */
    protected $_renderLayout = true;

    /**
     * Constructor
     *
     * Set the viewSuffix to "tpl.php" unless a viewSuffix option is
     * provided in the $options parameter. And set the stack to the default
     *
     * @param  Zend_View_Interface $view
     * @param  array $options
     * @return void
     */
    public function __construct(Zend_View_Interface $view = null, array $options = array())
    {
        if (!isset($options['viewSuffix'])) {
            $options['viewSuffix'] = 'tpl.php';
        }
        parent::__construct($view, $options);

        $this->_stack = $this->_defaultStack;
    }

    /**
     * Add a layout to the stack
     *
     * Add a layout to be rendered inside the current stack
     * of layouts
     *
     * @see Zend_Controller_Action_Helper_ViewRenderer::_translateSpec()
     *
     * @param	string	The filename, possibly using variables as described for _translateSpec(...)
     *
     * @return	boolean	The same as array_unshift
     */
    public function addLayout($script)
    {
        return array_unshift($this->_stack, $script);
    }

    /**
     * Clears the layout stack, removing the default stack and any
     * layouts that have been added.
     *
     * @return array	The previous layout stack
     */
    public function clearLayout()
    {
        $stack = $this->_stack;
        $this->_stack = array();
        return $stack;
    }

    /**
     * Render the action script, then each of the layouts in the stack in
     * order, assigning the content of each to the content variable in
     * the next. Append the result to the Response's body.
     *
     * @param string $script
     * @param string $name		The response segment to append to
     */
    public function renderScript($script, $name = null)
    {
        $this->view->baseUrl = $this->_request->getBaseUrl();
        if (null === $name) {
            $name = $this->getResponseSegment();
        }

        // get the content of the action
        $content = $this->view->render($script);
        $this->view->content = $content;

        if ($this->_renderLayout) {
            foreach ($this->_stack as $layout) {
                $layoutScript = $this->_translateSpec($layout.'.:suffix');
                $content = $this->view->render($layoutScript);
                $this->view->content = $content;
	        }
        }

        $this->getResponse()->appendBody($content, $name);
    }

    /**
     * Enable, or disable the rendering of layouts
     *
     * @param boolean	Whether the layouts should be rendered or not
     */
    public function setLayoutEnabled($enabled = true)
    {
        $this->_renderLayout = $enabled;
    }
}

The implementation in the view script is very similar to the previous incarnation, except the view is no longer responsible for rendering the children itself, this is done beforehand, and is passed to the layout in the "content" variable. Leaving the layout to look something like:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title><?php echo (isset($this->title)) ? $this->escape($this->title) : 'Untitled'; ?></title>
</head>
    <body>
        <?php echo $this->content; ?>
    </body>
</html>

And, unlike the previous version, all levels of layout work the same way. So the controller layout would look like this:

<h1>Default Controller</h1>
<h2><?php echo $this->escape($this->title); ?></h2>
<?php echo $this->content; ?>

To use these layouts, they can be added to the stack, like so (from the controller):

$this->_helper->viewRenderer->clearLayout(); // clear the existing layouts
$this->_helper->viewRenderer->addLayout(':controller/layout');
$this->_helper->viewRenderer->addLayout('layout');

But this is the default anyway.

Until the Zend framework standardizes the layout situation, with Zend_View_Enhanced or Zend_Layout, or any combination thereof, this method will feature in my applications.

post Extending Zend Framework’s ViewRenderer Helper to Allow Application and Controller Page Layouts

June 26th, 2007

Filed under: Code, PHP, Zend Framework — Brenton Alker @ 01:48

I intend to do a write-up of my experiences choosing a PHP framework, but for now, I just thought I would share this.

One of the out-of-the-box features that the Zend Framework is missing is "layouts". These are the templates that contain the elements that are present on every (or at least almost every) page of the application. Things like headers, main menu’s, footers etc.

I looked at a number of different ways to implement this in Zend, the most obvious idea involves adding header and footer calls (possible a helper) in every view. While this will work, it is a lot of duplicated code.

Maugrim the Reaper’s blog contains a fairly comprehensive investigation (in 6 parts so far, the 7th hopefully coming soon) into "Complex Views". Unfortunately, there is not (as yet) any solid implementation of most of the concepts available there, but it does provide much to think about.

The most elegant implementation (IMHO) of a layout system to meet my needs that I have found (and believe me I’ve looked) comes from Akra’s Devnotes and involves extending the ViewRenderer Action Helper to render the layout template.

This works very similarly to my initial stab at an implementation, but does so in a much cleaner way (I initially had separate top and bottom templates, instead of the call to render the content in the layout). However, it does lack one feature that my implementation had, it only allows application level layouts. I also implemented controller level layouts; A 3-level view structure.

This allows the controllers to contain their own structure within the applications main structure. I extended Rob’s class (as in added to, not in the OO sense) to incorporate this 3-level view structure.

My current code looks like this:

<?php
class Custom_Controller_Action_Helper_ViewRenderer extends Zend_Controller_Action_Helper_ViewRenderer
{
    /**
     * Name of layout script to render. Defaults to 'layout'.
     *
     * @var string
     */
    protected $_layoutScript = 'layout.tpl.php';

    /**
     * Constructor
     *
     * Set the viewSuffix to "tpl.php" unless a viewSuffix option is
     * provided in the $options parameter.
     *
     * @param  Zend_View_Interface $view
     * @param  array $options
     * @return void
     */
    public function __construct(Zend_View_Interface $view = null,
                                array $options = array())
    {
        if (!isset($options['viewSuffix'])) {
            $options['viewSuffix'] = 'tpl.php';
        }
        parent::__construct($view, $options);
    }

    /**
     * Set the layout script to be rendered.
     *
     * @param string $script
     */
    public function setLayoutScript($script)
    {
        $this->_layoutScript = $script;
    }

    /**
     * Retreive the name of the layout script to be rendered.
     *
     * @return string
     */
    public function getLayoutScript()
    {
        return $this->_layoutScript;
    }

    /**
     * Render the action script and assign the the view for use
     * in the layout script. Render the layout script and append
     * to the Response's body.
     *
     * @param string $script
     * @param string $name
     */
    public function renderScript($script, $name = null)
    {
        $this->view->baseUrl = $this->_request->getBaseUrl();
        if (null === $name) {
            $name = $this->getResponseSegment();
        }

        //assign controller script name to view
        $this->view->controllerScript = $this->_translateSpec(':controller/' . $this->_layoutScript);

        // assign action script name to view.
        $this->view->actionScript = $script;

        // render layout script and append to Response's body
        $layoutScript = $this->getLayoutScript();
        $layoutContent = $this->view->render($layoutScript);
        $this->getResponse()->appendBody($layoutContent, $name);

        $this->setNoRender();
    }
}

As an example of its use:

The Application Layout (views/scripts/layout.tpl.php) looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title><?php echo (isset($this->title)) ? $this->escape($this->title) : 'Untitled'; ?></title>
</head>
    <body>
        <?php echo $this->render($this->controllerScript); ?>
    </body>
</html>

The Controller (for the default "index" controller) layout (views/scripts/index/layout.tpl.php) looks like this:

<h1>Default Controller</h1>
<h2><?php echo $this->escape($this->title); ?></h2>
<?php echo $this->render($this->actionScript); ?>

And that of course leaves the action’s view to only display the items specific to that action.

UPDATE:
This code is broken. During some debugging, I discovered that this method of calling the render function from the view causes the action function to be run twice. It also prevents the action function (or view) from manipulating variables that can be used by the layout script, so preventing things like the action requesting javascript or stylesheet includes in the html head section.

I have re-worked the script to overcome these problems, and will post it soon.

ruldrurd
Powered by WordPress, Web Design by Laurentiu Piron
Entries (RSS) and Comments (RSS)