Doctrine is not just an ORM for Relational Databases post

Posted on 2014-01-18 by jwage


In April of 2010 the first commit for the Doctrine MongoDB ODM project was made. I was experimenting with MongoDB at the time and I wanted to see how difficult it would be to build a version of Doctrine for MongoDB.

Up until the MongoDB ODM, Doctrine was solely a project built around the DBAL/ORM and was advertised as such. In May of 2010 we decided to widen the scope of the project so that we could host libraries like the MongoDB ODM. This change led to a spur of new contributors and development and we now have several object mappers developed under Doctrine and things are more active than ever.

Below is an overview of all the libraries underneath the Doctrine project.

Common Shared Libraries

doctrine/common

Doctrine Common contains some base functionality and interfaces you need in order to create a Doctrine style object mapper. All of our mapper projects follow the same Doctrine\Common\Persistence interfaces. Here are the ObjectManager and ObjectRepository interfaces:

<?php

namespace Doctrine\Common\Persistence

interface ObjectManager
{
    public function find($className, $id);
    public function persist($object);
    public function remove($object);
    public function merge($object);
    public function clear($objectName = null);
    public function detach($object);
    public function refresh($object);
    public function flush();
    public function getRepository($className);
}

interface ObjectRepository
{
    public function find($id);
    public function findAll();
    public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null);
    public function findOneBy(array $criteria);
}

doctrine/collections

Doctrine Collections is a library that contains classes for working with arrays of data. Here is an example using the simple Doctrine\Common\Collections\ArrayCollection class:

<?php

$data = new \Doctrine\Common\Collections\ArrayCollection(array(1, 2, 3));
$data = $data->filter(function($count) { return $count > 1; });

doctrine/annotations

Doctrine Annotations is a library that allows you to parse structured information out of a doc block.

Imagine you have a class with a doc block like the following:

<?php

/** @Foo(bar="value") */
class User
{

}

You can parse the information out of the doc block for User easily. Define a new annotation object:

<?php

/**
 * @Annotation
 * @Target("CLASS")
 */
class Foo
{
    /** @var string */
    public $bar;
}

Now you can get instances of Foo defined on the User:

<?php

$reflClass = new ReflectionClass('User');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$classAnnotations = $reader->getClassAnnotations($reflClass);

foreach ($classAnnotations AS $annot) {
    if ($annot instanceof Foo) {
        echo $annot->bar; // prints "value";
    }
}

doctrine/inflector

Doctrine Inflector is a library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.

<?php

$camelCase = 'camelCase';
$table = \Doctrine\Common\Inflector::tableize($camelCase);
echo $table; // camel_case

doctrine/lexer

Doctrine Lexer is a library that can be used in Top-Down, Recursive Descent Parsers. This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL).

Here is what the AbstractLexer provided by Doctrine looks like:

<?php

namespace Doctrine\Common\Lexer;

abstract class AbstractLexer
{
    public function setInput($input);
    public function reset();
    public function resetPeek();
    public function resetPosition($position = 0);
    public function isNextToken($token);
    public function isNextTokenAny(array $tokens);
    public function moveNext();
    public function skipUntil($type);
    public function isA($value, $token);
    public function peek();
    public function glimpse();
    public function getLiteral($token);

    abstract protected function getCatchablePatterns();
    abstract protected function getNonCatchablePatterns();
    abstract protected function getType(&$value);
}

To implement a lexer just extend the Doctrine\Common\Lexer\AbstractParser class and implement the getCatchablePatterns, getNonCatchablePatterns, and getType methods. Here is a very simple example lexer implementation named CharacterTypeLexer. It tokenizes a string to T_UPPER, T_LOWER and T_NUMER:

<?php

use Doctrine\Common\Lexer\AbstractParser;

class CharacterTypeLexer extends AbstractLexer
{
    const T_UPPER =  1;
    const T_LOWER =  2;
    const T_NUMBER = 3;

    protected function getCatchablePatterns()
    {
        return array(
            '[a-bA-Z0-9]',
        );
    }

    protected function getNonCatchablePatterns()
    {
        return array();
    }

    protected function getType(&$value)
    {
        if (is_numeric($value)) {
            return self::T_NUMBER;
        }

        if (strtoupper($value) === $value) {
            return self::T_UPPER;
        }

        if (strtolower($value) === $value) {
            return self::T_LOWER;
        }
    }
}

Use CharacterTypeLexer to extract an array of upper case characters:

<?php

class UpperCaseCharacterExtracter
{
    private $lexer;

    public function __construct(CharacterTypeLexer $lexer)
    {
        $this->lexer = $lexer;
    }

    public function getUpperCaseCharacters($string)
    {
        $this->lexer->setInput($string);
        $this->lexer->moveNext();

        $upperCaseChars = array();
        while (true) {
            if (!$this->lexer->lookahead) {
                break;
            }

            $this->lexer->moveNext();

            if ($this->lexer->token['type'] === CharacterTypeLexer::T_UPPER) {
                $upperCaseChars[] = $this->lexer->token['value'];
            }
        }

        return $upperCaseChars;
    }
}

$upperCaseCharacterExtractor = new UpperCaseCharacterExtracter(new CharacterTypeLexer());
$upperCaseCharacters = $upperCaseCharacterExtractor->getUpperCaseCharacters('1aBcdEfgHiJ12');

print_r($upperCaseCharacters);

The variable $upperCaseCharacters contains all of the upper case characters:

Array
(
    [0] => B
    [1] => E
    [2] => H
    [3] => J
)

doctrine/cache

Doctrine Cache is a library that provides an interface for caching data. It comes with implementations for some of the most popular caching data stores. Here is what the Cache interface looks like:

<?php

namespace Doctrine\Common\Cache;

interface Cache
{
    function fetch($id);
    function contains($id);
    function save($id, $data, $lifeTime = 0);
    function delete($id);
    function getStats();
}

Here is an example using memcache:

<?php

$memcache = new \Memcache();
$cache = new \Doctrine\Common\Cache\MemcacheCache();
$cache->setMemcache($memcache);

$cache->set('key', 'value');

echo $cache->get('key') // prints "value"

Other supported drivers are:

  • APC
  • Couchbase
  • Filesystem
  • Memcached
  • MongoDB
  • PhpFile
  • Redis
  • Riak
  • WinCache
  • Xcache
  • ZendData

Database Abstraction Layers

doctrine/dbal

Doctrine DBAL is a library that provides an abstraction layer for relational databases in PHP. Read Doctrine DBAL: PHP Database Abstraction Layer blog post for more information on the DBAL.

doctrine/mongodb

Doctrine MongoDB is a library that provides an abstraction layer on top of the PHP MongoDB PECL extension. It provides some additional functionality and abstractions to make working with MongoDB easier.

doctrine/couchdb-client

Doctrine CouchDB Client is a library that provides a connection abstraction to CouchDB by wrapping around the CouchDB HTTP API.

<?php

$client = \Doctrine\CouchDB\CouchDBClient::create();

array($id, $rev) = $client->postDocument(array('foo' => 'bar'));
$client->putDocument(array('foo' => 'baz'), $id, $rev);

$doc = $client->findDocument($id);

Object Mappers

The object mappers are where all the pieces come together. The object mappers provide transparent persistence for PHP objects. As mentioned above, they all implement the common interfaces from Doctrine\Common so working with each of them is generally the same. You have an ObjectManager to manage the persistent state of your domain objects:

<?php

$user = new User();
$user->setId(1);
$user->setUsername('jwage');

$om = $this->getYourObjectManager();
$om->persist($user);
$om->flush(); // insert the new document

Then you can find that object later and modify it:

<?php

$user = $om->find('User', 1);
echo $user->getUsername(); // prints "jwage"

$user->setUsername('jonwage'); // change the obj in memory

$om->flush(); // updates the object in the database

You can find more information about the supported object mappers below:

doctrine/orm

Doctrine ORM provides persistence for PHP objects to relational database.

doctrine/couchdb-odm

Doctrine CouchDB ODM provides persistence for PHP objects to CouchDB.

doctrine/phpcr-odm

Doctrine PHPCR (Content Repository) ODM provides persistence to a backend like Jackalope or Midgard2. This is a specialized object mapper for dealing with data designed for building content websites. Think of this like a backend for a CMS (Content Management System).

doctrine/mongodb-odm

Doctrine MongoDB ODM provides persistence for PHP objects to MongoDB. You can read more about the MongoDB ODM here.

doctrine/orientdb-odm

Doctrine OrientDB ODM provides persistence for PHP objects to OrientDB.

Categories: articles