Deprecated Behaviour

The inane, sometimes insane, ramblings from the mind of Brenton Alker.

Environment Specific Bootstrapping for Zend Framework

When you build an application, it is often deployed to a number of servers each with a different set of configurations. The development and production servers, with different debug, logging, and database details is an obvious example. Many approaches I have seen to dealing with this, including Anis uddin Ahmad’s post that inspired this one, involve determining the domain the application is running on, and loading a pre-determined configuration for that host.

There are 2 downfalls to that solution from my point of view;

  1. Each new environment involves editing the source of the application

  2. If the application involves command line components, there is no way to determine the domain, requiring a special case

My solution may look familiar if you have ever looked at python’s django. It involves using environment variables, which can be set through the web server, or on the command line – even in crontab. Basically, I set an environment variable specifying the location of the configuration file to load into the application. I actually allow a list of configuration files,so you can have a “base” and only override specific configuration as required per location.

To add and environment variable in apache, you use the mod_env module’s SetEnv directive.

SetEnv APPLICATION_CONFIG /path/to/config/basic.php:/path/to/config/development.php

on the command line, you can use export.

export APPLICATION_CONFIG=/path/to/config/basic.php:/path/to/config/development.php

and very similarly in crontab (above the code that requires it)

APPLICATION_CONFIG=/path/to/config/basic.php:/path/to/config/development.php

To read the configuration files, from php we can simple get the environment variable with getenv(), then merge each configuration into the applications configuration;

1
2
3
4
5
6
7
8
9
10
<?php

$configFiles = getenv('APPLICATION_CONFIG');

$configArray = explode(PATH_SEPARATOR, $configFiles);
$config = new Zend_Config(array(), true);
foreach ($configArray as $newConfig) {
$config->merge(new Zend_Config(require $newConfig));
}
$config->setReadOnly();

In this example I am using simple PHP files to store the configuration arrays, you could also use any other format Zend_Config can read. You could also use a simple array and array_merge() instead on Zend_Config if you are not using the framework.

Data Mapper Pattern in PHP

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?php
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<?php
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.

1
2
3
4
5
6
7
8
9
10
11
<?php

// 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.

Telecommute Experience (Part 2)

I have been telecommuting for a bit over a month now, and things have settled quite well.

We have set up an office, away from the entertainment area (except the foosball table, that’s in the office :)). This has been good to keep some separation between those trying to get work done and the rest of the household population.

With the time difference, office hours work well. An 8 hour stint from 1pm to 9pm (Canada) is the same as 7am to 3pm (Australia). Giving plenty of time in the morning to enjoy the snow, while still allowing ample real-time communication with the office during business hours, and even leaving some time after the work day to relax.

The work itself hasn’t changed much. A good issue tracker makes things much easier (We use MySQL’s Eventum) because everyone can easily see a current list of priorities and who is doing what, which is always important but even more so without real-time contact.

A good local development environment has also been invaluable, being able to develop and test completely without a connection to the world ensures I can still be productive even when the Internet is on the fritz. Using virtual machines I have set up individual environments for each project. Git (via git-svn) for version control allows me to work; branching, committing, merging and reverting all locally.

Communication has primarily been via instant messenger, which was already the case in the office anyway, so no real transition there. Skype is also great if you need voice communication (I generally don’t).

Using these tools, the transition has been smooth and painless. We have even successfully deployed a major project since I have been here. So far, so good.