Tuesday, March 2, 2010

Creating A Doctrine Custom Behaviour, Part 2

In part 1 we made a simple custom behaviour and then gave it some options we could customize in our schema. The second half of the power of Doctrine's behaviours though is being able to set values. First, uncomment this line in the last example in part 1:

$this->addListener(new DarrenTestableListener($this->_options));


You should get told class DarrenTestableListener does not exist, so create DarrenTestableListener.php in the same directory as DarrenTestable.php (though, as mentioned before, it can go anywhere in your models directory tree), and fill it with this code:

class DarrenTestableListener extends Doctrine_Record_Listener
{
protected $_options = array();

public function __construct(array $options)
{
$this->_options = $options;
}

/** Called when a new record is created */
public function preInsert(Doctrine_Event $event)
{
$name = $event->getInvoker()->getTable()->getFieldName($this->_options['name']);
$modified = $event->getInvoker()->getModified();
if ( ! isset($modified[$name])) {
$event->getInvoker()->$name = "C";
}
}

/** Called when an existing record is updated */
public function preUpdate(Doctrine_Event $event)
{
$name = $event->getInvoker()->getTable()->getFieldName($this->_options['name']);
$modified = $event->getInvoker()->getModified();
if ( ! isset($modified[$name])) {
$event->getInvoker()->$name .= 'U';
}
}

/** Handle dql update queries */
public function preDqlUpdate(Doctrine_Event $event)
{
$params = $event->getParams();
$name = $event->getInvoker()->getTable()->getFieldName($this->_options['name']);
$field = $params['alias'] . '.' . $name;
$query = $event->getQuery();
if ( ! $query->contains($field)) {
$query->set($field, '?', 'U');
}
}

}


It has default options (and a constructor to set the options), then one function to handle INSERTs and two functions to handle UPDATEs. (There are also hooks for DELETE, save and validation.) All three functions follow a similar pattern:

  1. Get the real fieldname
  2. See if we need to do anything
  3. If so, set the field


What we do is set the field to "C" when it is created. Then each time it is updated we append a "U". E.g. after three updates it will look like "CUUU". (TODO: I'm not so familiar with DQL, and I am not using it, so the DQL version replaces the existing data instead of appending. If you can supply the proper code for it let me know!)

Incidentally if you set a default in the options that will get precedence over your code (that is what the getModified() call is for, I assume). E.g. if I change my YAML schema to:

ActsAs:
DarrenTestable:
name: being_silly
options:
default: Hello!


Then after a couple of updates being_silly will contains "Hello!UU".

No comments: