Playing with Lithium
I finally found some time to play a little with Lithium, a brand new php 5.3 based framework. I did have a quick look when the first versions got released and already liked it back then, it's fast, clean and makes full use of the latest php features. One other reason for jumping in was its support for MongoDb, a fast schema free database. When working my way through the obligatory blog sample I ended up with the following for reading a MongoDb entry by id in the controller view action:
$post = Post::find('first', array('conditions' => array('_id' => new MongoId($id))));
This works perfectly well, and is explicit, which I like. Still, I'd like to abstract away the MongoDb specifics, and have a shorthand call like Post::read($id) available for reading a single item. Lithium doesn't offer this but it has a powerful filtering system which enables passing closures to modify behaviour at runtime. It's very flexible and as I see a good example of aspect oriented programming. For creating the shorthand function I had to create two filters, one for converting the string id into a MongoId instance, the other is a 'finder' filter, enabling the read() call.
<?php
namespace uw_posts\models;
use \MongoId;
class Post extends \lithium\data\Model
{
/**
* Set up default connection options and connect default finders.
*
* Parent override which registers:
*
* <ul>
* <li>a find filter for coverting a string id to a MongoId instance</li>
* <li>a 'read' finder, which enables <code>Model::read($id)</code></li>
* </ul>
*
* @see lithium\data\Model
* @param array $config
* @return void
*/
public static function __init($config = array())
{
parent::__init($config);
// filter for converting a string id into a MongoId instance
static::applyFilter('find', function($self, $params, $chain){
$conditions = $params['options']['conditions'];
if (isset($conditions['id']) and preg_match('/^[0-9a-f]{24}$/', $conditions['id']))
{
$params['options']['conditions']['_id'] = new MongoId($conditions['id']);
unset($params['options']['conditions']['id']);
}
return $chain->next($self, $params, $chain);
});
// read finder
static::finder('read', function($self, $params, $chain) {
$conditions = $params['options']['conditions'];
if (isset($conditions['_id']))
{
return $self::find('first', array('conditions' => array('id' => $conditions['_id'])));
}
return $chain->next($self, $params, $chain);
});
}
}
It's quite compact and efficient and keeps all functionality nicely isolated. One drawback of the 'read' finder filter as quickly implemented here is that it doesn't allow for other filters to be passed when the 'read' finder gets called. One can make every function filterable though, so going around this will be to create an explicit read() call in the model and making it filterable, \lithium\data\Model contains examples on how to do this.

