Introduction

Doctrine MongoDB Object Document Mapper is built for PHP 5.3.0+ and provides transparent persistence for PHP objects to the popular MongoDB database by 10gen.

Features Overview

  • Transparent persistence.
  • Map one or many embedded documents.
  • Map one or many referenced documents.
  • Create references between documents in different databases.
  • Map documents with Annotations, XML, YAML or plain old PHP code.
  • Documents can be stored on the MongoGridFS.
  • Collection per class(concrete) and single collection inheritance supported.
  • Map your Doctrine 2 ORM Entities to the ODM and use mixed data stores.
  • Inserts are performed using MongoCollection::batchInsert()
  • Updates are performed using atomic operators.

Here is a quick example of some PHP object documents that demonstrates a few of the features:

<?php
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

/** @ODM\MappedSuperclass */
abstract class BaseEmployee
{
    /** @ODM\Id */
    private $id;

    /** @ODM\Increment */
    private $changes = 0;

    /** @ODM\Collection */
    private $notes = array();

    /** @ODM\String */
    private $name;

    /** @ODM\Float */
    private $salary;

    /** @ODM\Date */
    private $started;

    /** @ODM\Date */
    private $left;

    /** @ODM\EmbedOne(targetDocument="Address") */
    private $address;

    // ...
}

/** @ODM\Document */
class Employee extends BaseEmployee
{
    /** @ODM\ReferenceOne(targetDocument="Documents\Manager") */
    private $manager;

    // ...
}

/** @ODM\Document */
class Manager extends BaseEmployee
{
    /** @ODM\ReferenceMany(targetDocument="Documents\Project") */
    private $projects = array();

    // ...
}

/** @ODM\EmbeddedDocument */
class Address
{
    /** @ODM\String */
    private $address;

    /** @ODM\String */
    private $city;

    /** @ODM\String */
    private $state;

    /** @ODM\String */
    private $zipcode;

    // ...
}

/** @ODM\Document */
class Project
{
    /** @ODM\Id */
    private $id;

    /** @ODM\String */
    private $name;

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

    // ...
}

Now those objects can be used just like you weren’t using any persistence layer at all and can be persisted transparently by Doctrine:

<?php

$employee = new Employee();
$employee->setName('Employee');
$employee->setSalary(50000.00);
$employee->setStarted(new \DateTime());

$address = new Address();
$address->setAddress('555 Doctrine Rd.');
$address->setCity('Nashville');
$address->setState('TN');
$address->setZipcode('37209');
$employee->setAddress($address);

$project = new Project('New Project');
$manager = new Manager();
$manager->setName('Manager');
$manager->setSalary(100000.00);
$manager->setStarted(new \DateTime());
$manager->addProject($project);

$dm->persist($employee);
$dm->persist($address);
$dm->persist($project);
$dm->persist($manager);
$dm->flush();

The above would batch insert the following:

Array
(
    [000000004b0a33690000000001c304c6] => Array
        (
            [name] => New Project
        )

)
Array
(
    [000000004b0a33660000000001c304c6] => Array
        (
            [changes] => 0
            [notes] => Array
                (
                )

            [name] => Manager
            [salary] => 100000
            [started] => MongoDate Object
                (
                    [sec] => 1275265048
                    [usec] => 0
                )

            [projects] => Array
                (
                    [0] => Array
                        (
                            [$ref] => projects
                            [$id] => 4c0300188ead0e947a000000
                            [$db] => my_db
                        )

                )

        )

)
Array
(
    [000000004b0a336a0000000001c304c6] => Array
        (
            [changes] => 0
            [notes] => Array
                (
                )

            [name] => Employee
            [salary] => 50000
            [started] => MongoDate Object
                (
                    [sec] => 1275265048
                    [usec] => 0
                )

            [address] => Array
                (
                    [address] => 555 Doctrine Rd.
                    [city] => Nashville
                    [state] => TN
                    [zipcode] => 37209
                )

        )

)

If we update a property and call ->flush() again we’ll get an efficient update query using the atomic operators:

<?php
$newProject = new Project('Another Project');
$manager->setSalary(200000.00);
$manager->addNote('Gave user 100k a year raise');
$manager->incrementChanges(2);
$manager->addProject($newProject);

$dm->persist($newProject);
$dm->flush();

The above could would produce an update that looks something like this:

Array
(
    [$inc] => Array
        (
            [changes] => 2
        )

    [$pushAll] => Array
        (
            [notes] => Array
                (
                    [0] => Gave user 100k a year raise
                )

            [projects] => Array
                (
                    [0] => Array
                        (
                            [$ref] => projects
                            [$id] => 4c0310718ead0e767e030000
                            [$db] => my_db
                        )

                )

        )

    [$set] => Array
        (
            [salary] => 200000
        )

)

This is a simple example, but it demonstrates well that you can transparently persist PHP objects while still utilizing the atomic operators for updating documents! Continue reading to learn how to get the Doctrine MongoDB Object Document Mapper setup and running!

Setup

Before we can begin, we’ll need to install the Doctrine MongoDB ODM library and its dependencies. The easiest way to do this is with Composer:

$ composer require "doctrine/mongodb-odm=~1.0.0-BETA10@dev"

Once ODM and its dependencies have been downloaded, we can begin by creating a bootstrap.php file in our project’s root directory, where Composer’s vendor/ directory also resides. Let’s start by importing some of the classes we’ll use:

<?php

use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Configuration;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;

The first bit of code will be to import Composer’s autoloader, so these classes can actually be loaded:

<?php

// ...

if ( ! file_exists($file = __DIR__.'/vendor/autoload.php')) {
    throw new RuntimeException('Install dependencies to run this script.');
}

$loader = require_once $file;

Note that instead of simply requiring the file, we assign its return value to the $loader variable. Assuming document classes will be stored in the Documents/ directory (with a namespace to match), we can register them with the autoloader like so:

<?php

// ...

$loader->add('Documents', __DIR__);

Ultimately, our application will utilize ODM through its DocumentManager class. Before we can instantiate a DocumentManager, we need to construct the Connection and Configuration objects required by its factory method:

<?php

// ...

$connection = new Connection();
$config = new Configuration();

Next, we’ll specify some essential configuration options. The following assumes that we will store generated proxy and hydrator classes in the Proxies/ and Hydrators/ directories, respectively. Additionally, we’ll define a default database name to use for document classes that do not specify a database in their mapping.

<?php

// ...

$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');
$config->setHydratorDir(__DIR__ . '/Hydrators');
$config->setHydratorNamespace('Hydrators');
$config->setDefaultDB('doctrine_odm');

The easiest way to define mappings for our document classes is with annotations. We’ll need to specify an annotation driver in our configuration (with one or more paths) and register the annotations for the driver:

<?php

// ...

$config->setMetadataDriverImpl(AnnotationDriver::create(__DIR__ . '/Documents'));

AnnotationDriver::registerAnnotationClasses();

At this point, we have everything necessary to construct a DocumentManager:

<?php

// ...

$dm = DocumentManager::create($connection, $config);

The final bootstrap.php file should look like this:

<?php

use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Configuration;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;

if ( ! file_exists($file = __DIR__.'/vendor/autoload.php')) {
    throw new RuntimeException('Install dependencies to run this script.');
}

$loader = require_once $file;
$loader->add('Documents', __DIR__);

$connection = new Connection();

$config = new Configuration();
$config->setProxyDir(__DIR__ . '/Proxies');
$config->setProxyNamespace('Proxies');
$config->setHydratorDir(__DIR__ . '/Hydrators');
$config->setHydratorNamespace('Hydrators');
$config->setDefaultDB('doctrine_odm');
$config->setMetadataDriverImpl(AnnotationDriver::create(__DIR__ . '/Documents'));

AnnotationDriver::registerAnnotationClasses();

$dm = DocumentManager::create($connection, $config);

That is it! Your DocumentManager instance is ready to be used!

Project Versions

Table Of Contents

This Page

Fork me on GitHub