MongoDB Migrations

This guide describes how to create and run mongodb migrations.

Introduction

From time to time there are structural or other changes to the data in the MongoDB persistence layer. When we make them we need a way to ensure that all our entities get updated.

While doctrine odm suggests that we do it manually by implementing migration code on the document level, this does not cut it for us since most of our document classes get generated by graviton. For this reason we support mongodb-migrations that are similar to regular doctrine orm migrations.

Preparing Bundles for Migrations

Each graviton bundle that contains migrations needs to be prepared in a specific way. It needs a migrations configuration and the path to the migrations directory needs to be added to the autoloader.

For the purpose of this documentation we are assuming that a bundle has the following structure.

.
|____composer.json
|____src
| |____Migrations
| | |____MongoDB
| |____Resources
| | | |____Version20151119140145.php
| | |____config
| | | |____migrations.yml
| | |____definition
| | | |____DocumentDefinition.json

The file migrations.yml configures the bundle level migration support.

# migrations.yml
---
name: acme-bundle migrations
collection_name: acme-bundle-migrations
migrations_namespace: AcmeBundle\Migrations\MongoDB
migrations_directory: vendor/grv/acme-bundle/src/Migrations/MongoDB

The namespaces of the migration classes is registered in composer.json.

# composer.json
{
    "autoload": {
        "psr-4": {"AcmeBundle\\": "src/"}
    }
}

With this the migration code knows where to find migration code as well as where to store metadata pertaining to the migrations.

Generating and Writing Migrations

To help you getting started with writing a migration you can use the console.

./vendor/graviton/graviton/app/console mongodb:migrations:generate \
        --configuration=vendor/acme/acme-bundle/src/Resources/config/migrations.yml

This generates a new migration class which needs to be customized to suite your migration needs. You will need to implement both the up() and down() method for a full migration.

If you need access to other parts of graviton during migrations you may implement the ContainerAwareInterface in your migration to get a container injected.

Annotated Example Annotation

<?php
/**
 * example migration
 */

namespace Graviton\EvojaChecklistBundle\src\Migrations\MongoDB;

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use AntiMattr\MongoDB\Migrations\AbstractMigration;
use Doctrine\MongoDB\Database;

/**
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
 * @license  http://opensource.org/licenses/MIT MIT License
 * @link     http://swisscom.ch
 */
class Version20151207122656 extends AbstractMigration implements ContainerAwareInterface
{
    /**
     * @var ContainerInterface container
     */
    private $container;

    /**
     * @param ContainerInterface|null $container
     * @return void
     */
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    /**
     * @return string
     */
    public function getDescription()
    {
        return "Migrate acme bundle things";
    }

    /**
     * @param Database $db mongodb database
     * @return void
     */
    public function up(Database $db)
    {
        // get dm (or other stuff) from container
        $dm = $this->container->get('doctrine.odm.mongodb.document_manager');

        // do migration (use hydrate(false) as needed)
        // ...
        // $qb = $dm->createQueryBuilder('GravitonDyn\AcmeBundle\Document\Document');
        // ...
 
        // flush dm at the end
        $dm->flush();
    }

    /**
     * @param Database $db mongodb database
     * @return void
     */
    public function down(Database $db)
    {
        // do something similar to up() but the other way around
    }
}

Running Migrations

You can run the migrations for a single bundle using the mongodb:migrations:migrate command as long as you pass the path to a configuration as described above.

./vendor/graviton/graviton/app/console mongodb:migrations:migrate \
        --configuration=vendor/acme/acme-bundle/src/Resources/config/migrations.yml

For your convenience you may also call graviton:mongodb:migrate which will run the migrate command for all configurations stored in the Resources/config directory of a bundle.

./vendor/graviton/graviton/app/console graviton:mongodb:migrate