Quickshiftin - Clever Crazy Code

Configuring Doctrine Migrations 2.0 via Zend_Config

October 20th, 2011

Using Doctrine2 for your new Zend project and Migrations 2.0 for database versioning? One aspect that quickly becomes annoying is the lack of integration with the standard Zend application.ini. Yes there is –configuration which allows you to specify configuration options via xml or yml, but those files aren’t aware of the many environments your Zend application is likely deployed to, nor do they know about handy shortcuts like APPLICATION_ENV, forcing you to hardcode absolute paths in each of several configuration files (one per environment).

Enough of the Madness

We finally got tired of dealing with the headaches in the stock Migrations setup and decided to integrate Zend_Config. It’s really nothing more than a new Configuration strategy and a quick patch to Doctrine\DBAL\Migrations\Tools\Console\Command\AbstractCommand. We’ll walk you through the setup.

Patching Migrations

Apply this patch to AbstractCommand to get started. It adds support for a ‘zend’ option to the –configuration parameter of the migrations command:

--- a/source/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php
+++ b/source/lib/Doctrine/DBAL/Migrations/Tools/Console/Command/AbstractCommand.php
@@ -105,8 +105,14 @@ abstract class AbstractCommand extends Command
             }
 
             if ($input->getOption('configuration')) {
-                $info = pathinfo($input->getOption('configuration'));
-                $class = $info['extension'] === 'xml' ? 'Doctrine\DBAL\Migrations\Configuration\XmlConfiguration' : 'Doctrine\DBAL\Migrations\Configuration\YamlConfiguration';
+                if($input->getOption('configuration') == 'zend')
+                    $class = 'Doctrine\DBAL\Migrations\Configuration\ZendConfiguration';
+                else {
+                    $info = pathinfo($input->getOption('configuration'));
+                    $class = $info['extension'] === 'xml' ?
+                        'Doctrine\DBAL\Migrations\Configuration\XmlConfiguration' :
+                        'Doctrine\DBAL\Migrations\Configuration\YamlConfiguration';
+                }
                 $configuration = new $class($conn, $outputWriter);
                 $configuration->load($input->getOption('configuration'));
             } else if (file_exists('migrations.xml')) {

Installing ZendConfiguration

Notice in the segment above the line where we reference Doctrine\DBAL\Migrations\Configuration\ZendConfiguration; we’ll need to have that installed if it’s going to work!

<?php
namespace Doctrine\DBAL\Migrations\Configuration;

/**
 * Load migration configuration information from
 * a Zend application.ini configuration file.
 *
 * @author Nathan Nobbe <nathan@moxune.com>
 */
class ZendConfiguration extends AbstractFileConfiguration
{
    /**
     * @inheritdoc
     */
    protected function doLoad($file)
    {
        $migrationsConfig = \Zend_Registry::get('config')
            ->resources->doctrine->migration;

        // set migrations name
        if(isset($migrationsConfig->name))
            $this->setName((string)$migrationsConfig->name);

        // set migrations table name
        if(isset($migrationsConfig->tableName))
            $this->setMigrationsTableName(
                (string)$migrationsConfig->tableName
            );

        // set migrations namespace
        if(isset($migrationsConfig->namespace))
            $this->setMigrationsNamespace(
                (string)$migrationsConfig->namespace
            );

        // set migrations directory
        // (assuming absolute path specificed via APPLICATION_PATH)
        if(isset($migrationsConfig->directory)) {
            $this->setMigrationsDirectory(
                $migrationsConfig->directory
            );
            $this->registerMigrationsFromDirectory(
                $migrationsConfig->directory
            );
        }

        // register custom migrations
        if(isset($migrationsConfig->migrations))
            foreach($migrationsConfig->migrations as $migration) {
                $this->registerMigration(
                    (string)$migration->version,
                    (string)$migration->class
                );
            }
    }
}

Configuring Zend Framework & doctrine.php

In order for ZendConfiguration to work properly, you’ll need a method like this in your Bootstrap.php:

protected function _initConfig()
{    
    $config = new Zend_Config($this->getOptions(), true);
    Zend_Registry::set('config', $config);
    return $config;
}

Then in the file you’re using to bootstrap Zend Framework and Doctrine on the cli, you’ll need to ensure the config method from Bootstrap.php is getting invoked, something like this:

// Bootstrapping resources
$bootstrap = $application->bootstrap(
    array('Doctrine', 'config')
)->getBootstrap();

Configuring Migrations with application.ini

Now that we have all of the boilerplate setup behind us we can move on to actually using it! In application.ini, you can now configure Migrations in similar vein to the stock xml or yml options.

; ------------------------------------------------------------------------------
; Doctrine Migrations Configuration
; ------------------------------------------------------------------------------
resources.doctrine.migration.name = "MyProject.com (development) Database Migrations"
resources.doctrine.migration.tableName = "doctrine_migration_versions"
resources.doctrine.migration.namespace = "DoctrineMigrations"
resources.doctrine.migration.directory = APPLICATION_PATH "/../library/MyLib/Migrations"
resources.doctrine.migration.migrations.migration1.version = "20111020073237"
resources.doctrine.migration.migrations.migration1.class = "DoctrineMigrations\VersionNewMigration"

Version nomenclature side note

Although the Migrations documentation suggests you can name custom migrations textual things like ‘VersionNewMigration’, we’d advise you stick to the standard convention of naming the migration after the timestamp-based version. This is due to what appears to be an oversight when running migrations:status –show-versions as you can see here from the output:

 == Migration Versions

    >> NewM-ig-ra ti:on: (NewMigration)                not migrated
    >> 2011-09-08 16:41:55 (20110908164155)                migrated

Putting the new feature to work

Now that you’ve added support for Zend_Config to Migrations and added configuration parameters in application.ini simply pass –configuration=zend when you invoke migration commands

php scripts/doctrine.php --configuration=zend migrations:status

Now you’re leveraging Zend_Config, which has several advantages

  • One configuration file that supports multiple environments
  • Leverage ZF constants like APPLICATION_ENV inside the configuration file, thus avoiding the need to hardcode absolute paths in each of the Migrations configuration files
  • Leverage the cascade effect of Zend_Config, so that you don’t have to duplicate information across Migrations configuration in various environments

Share

If you enjoyed this post please consider sharing it!

  • Twitter
  • LinkedIn
  • Facebook
  • Reddit
  • Digg

4 Responses to Configuring Doctrine Migrations 2.0 via Zend_Config

  1. asciimo says:

    Thank you so much for this article. I just started working on a Zend/Doctrine project and had to implement migrations. I expected Doctrine to offer this sort of functionality out-of-the-box, but nope. I’m pleased that you have shared such an elegant solution highlighting Doctrine’s extensibility.

  2. Nathan Nobbe says:

    @asciimo, glad you liked the article! We’ve been really busy at Moxune lately, but are planning to come back with some fresh blog posts soon, stay tuned!

  3. Wilmer says:

    Hi, good article… Actually I’m working in a project with Doctrine 2 and ZF2, maybe you can update your article with zf2.. Thank you!!

    • Nathan Nobbe says:

      Thanks for the feedback. D2 is nice, but also bulky.., ZF2 frankly scares me, so unfortunately, I’m not sure they’ll be an update with that combo. If you venture into it though, hit us up with a link!

Leave a Response