PHP: Working with objects on data bases using PDO and Mongo + Demo using Twig

02 Jun 2010

Working with database results as if they were objects allow to work intuitively and convenient wat. PDO has native support to return class instances instead of array. But with MongoDB is not that much harder to get an object iterator for a specific class. Here I’m placing an example using PDO, Mongo and Twig. With Twig you can see how cool is to access objects and calling methods producing derived data without having to generate them explicity (lazily on demand).

I have created an IteratorIterator derivate class and a static method using late static binding in order to cast an array/object into a new instance of the specified class.

class CastModelIteratorIterator extends IteratorIterator {  
    public $class;  

    function current() {  
        return Model::cast(parent::current(), $this->class);  
    }  
}  

class Model {  
    static public function cast($array, $class = null) {  
        $object = ($class === null) ? (new static) : (new $class);  
        foreach ($array as $k => $v) $object->$k = $v;  
        return $object;  
    }  

    static public function castIterator($iterator) {  
        $iterator = new CastModelIteratorIterator($iterator);  
        $iterator->class = get_called_class();  
        return $iterator;  
    }  
}  

class TestModel extends Model {  
    public $name, $pass;  

    function __construct($name = 'test') {  
        $this->name = $name;  
        $this->pass = self::hashPassword('test');  
    }  

    static function hashPassword($password) {  
        return md5("{$password}*");  
    }  

    function checkPassword($password) {  
        return $this->pass == self::hashPassword($password);  
    }  

    function url() {  
        return sprintf('/user/%s', urlencode($this->name));  
    }  
}  

// Sqlite PDO Test  
{  
    $db = new PDO('sqlite::memory:');  
    $db->query('CREATE TABLE TestModel (name, pass);');  
    $t = $db->prepare('INSERT INTO TestModel (name, pass) VALUES (?, ?);');  
    $model = new TestModel('Test');  
    $t->execute(array($model->name, $model->pass));  
    foreach ($db->query('SELECT * FROM TestModel;', PDO::FETCH_CLASS, 'TestModel') as $e) {  
        printf("%s\n", $e->url());  
    }  
}  

// Mongo Test  
{  
    $mongo = new Mongo(); // connect  
    $db = $mongo->demo;  
    $collection = $db->TestModel;  
    $collection->remove();  
    $collection->insert(new TestModel('Test'));  

    foreach (TestModel::castIterator($collection->find()) as $e) {  
        printf("%s\n", $e->url());  
    }  
}  

// Twig  
{  
    require_once(__DIR__ . '/Twig/Autoloader.php');  
    Twig_Autoloader::register();  
    $collection->insert(new TestModel('Demo'));  

    $twig = new Twig_Environment(new Twig_Loader_String(), array(  
        'debug'       => true,  
        'auto_reload' => true,  
    ));  

    $twig->loadTemplate('  
        <ul>  
        {{ "{%" }} for item in list %}  
            <li><a href="{{ "{{" }} item.url }}">User {{ "{{" }} item.name }}</a></li>  
        {{ "{%" }} endfor %}  
        </ul>  
    ')->display(array(  
        'list' => TestModel::castIterator($collection->find()),  
    ));  
}  

Spanish

Trabajar con resultados de bases de datos como si fuesen objetos permite trabajar de una forma muy cómoda e intuitiva. PDO tiene soporte nativo para devolver instancias de una clase en vez de arrays. Pero con MongoDB tampoco es mucho más complicado conseguir un iterador de objetos de una clase determinada.
Aquí coloco un ejemplo con PDO, Mongo y Twig. Con twig se ve claramente la comodidad de tener objetos con los que poder llamar métodos que produzcan datos derivados sin tenerlos que generar explícitamente (on demand).
He creado una clase derivada de IteratorIterator y un método estático usando late static binding para poder castear un array/objeto a una nueva instancia de una clase especificada.

class CastModelIteratorIterator extends IteratorIterator {  
    public $class;  

    function current() {  
        return Model::cast(parent::current(), $this->class);  
    }  
}  

class Model {  
    static public function cast($array, $class = null) {  
        $object = ($class === null) ? (new static) : (new $class);  
        foreach ($array as $k => $v) $object->$k = $v;  
        return $object;  
    }  

    static public function castIterator($iterator) {  
        $iterator = new CastModelIteratorIterator($iterator);  
        $iterator->class = get_called_class();  
        return $iterator;  
    }  
}  

class TestModel extends Model {  
    public $name, $pass;  

    function __construct($name = 'test') {  
        $this->name = $name;  
        $this->pass = self::hashPassword('test');  
    }  

    static function hashPassword($password) {  
        return md5("{$password}*");  
    }  

    function checkPassword($password) {  
        return $this->pass == self::hashPassword($password);  
    }  

    function url() {  
        return sprintf('/user/%s', urlencode($this->name));  
    }  
}  

// Sqlite PDO Test  
{  
    $db = new PDO('sqlite::memory:');  
    $db->query('CREATE TABLE TestModel (name, pass);');  
    $t = $db->prepare('INSERT INTO TestModel (name, pass) VALUES (?, ?);');  
    $model = new TestModel('Test');  
    $t->execute(array($model->name, $model->pass));  
    foreach ($db->query('SELECT * FROM TestModel;', PDO::FETCH_CLASS, 'TestModel') as $e) {  
        printf("%s\n", $e->url());  
    }  
}  

// Mongo Test  
{  
    $mongo = new Mongo(); // connect  
    $db = $mongo->demo;  
    $collection = $db->TestModel;  
    $collection->remove();  
    $collection->insert(new TestModel('Test'));  

    foreach (TestModel::castIterator($collection->find()) as $e) {  
        printf("%s\n", $e->url());  
    }  
}  

// Twig  
{  
    require_once(__DIR__ . '/Twig/Autoloader.php');  
    Twig_Autoloader::register();  
    $collection->insert(new TestModel('Demo'));  

    $twig = new Twig_Environment(new Twig_Loader_String(), array(  
        'debug'       => true,  
        'auto_reload' => true,  
    ));  

    $twig->loadTemplate('  
        <ul>  
        {{ "{%" }} for item in list %}  
            <li><a href="{{ "{{" }} item.url }}">User {{ "{{" }} item.name }}</a></li>  
        {{ "{%" }} endfor %}  
        </ul>  
    ')->display(array(  
        'list' => TestModel::castIterator($collection->find()),  
    ));  
}