Pragmatism in the real world

Using Doctrine Migrations as a standalone tool

My current project has reached the point where a good migrations system is required. As I’m targeting two different database engines (MySQL and MS SQL Server) and we’re already using DBAL, it made sense to use Migrations from the Doctrine project.

To do this, I updated my composer.json with the following require statements:

"doctrine/migrations": "1.0.*@dev",
"symfony/console": "~2.5"

in order to install Migrations and also Symfony console component so that I can run it from the command line.

The CLI script

Migrations doesn’t come with it’s own CLI script, so you have to create your own. Fortunately, we can tweak the supplied phar-cli-stub.php. I placed it in bin/migrations.php:

/**
 * Command line script to run Migrations
 * Inspired by phar-cli-stup.php
 */

use Symfony\Component\Console;
use Doctrine\DBAL\Migrations\MigrationsVersion;
use Doctrine\DBAL\Migrations\Tools\Console\Command as MigrationsCommand;

require (__DIR__ . '/../vendor/autoload.php');

// Set current directory to application root so we can find config files
chdir(__DIR__ . '/..');

// Instantiate console application
$cli = new Console\Application('Doctrine Migrations', MigrationsVersion::VERSION());
$cli->setCatchExceptions(true);

$helperSet = new Console\Helper\HelperSet();
$helperSet->set(new Console\Helper\DialogHelper(), 'dialog');
$cli->setHelperSet($helperSet);

// Add Migrations commands
$commands = array();
$commands[] = new MigrationsCommand\ExecuteCommand();
$commands[] = new MigrationsCommand\GenerateCommand();
$commands[] = new MigrationsCommand\LatestCommand();
$commands[] = new MigrationsCommand\MigrateCommand();
$commands[] = new MigrationsCommand\StatusCommand();
$commands[] = new MigrationsCommand\VersionCommand();

// remove the "migrations:" prefix on each command name
foreach ($commands as $command) {
    $command->setName(str_replace('migrations:', '', $command->getName()));
}
$cli->addCommands($commands);

// Run!
$cli->run();

Note that, by default, the console commands are all prefixed with ‘migrations:’. This makes sense as usually they are integrated with the rest of the Doctrine ORM command line application and so it avoids any clashes. In this case, this script is only dealing with migrations and I don’t want to type more than I have to, so I remove the prefixes!

Configuration

Migrations requires two separate configuration files to work: migrations.yml and migrations-db.php. These need to be in the current directory, so I used chdir() to ensure that it is in a known location – the application root in this case.

migrations.yml is the config file that tells Migrations the names of things:

migrations.yml:

name: DBAL Migrations
migrations_namespace: Migrations
table_name: migration_versions
migrations_directory: migrations

In my case, I want to use my migrations files to be in a directory called migrations and use the namespace Migrations. The database table name that Migrations uses to keep track of its internal status is called migration_versions. I’m not too imaginative when it comes to naming things!

migrations-db.php must return an array that is used to configure Doctrine\DBAL\DriverManager::getConnection()

migrations-db.php:

<?php
return array(
        'dbname'   => 'albums',
        'user'     => 'rob',
        'password' => '123456',
        'host'     => 'localhost',
        'driver'   => 'pdo_mysql',
);

The documentation will help you find out what can go in here.

We’re now ready to go!

On the command line, php bin/migrations.php status should work:

$ php bin/migrations.php 
Doctrine Migrations version 2.0.0-DEV

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v|vv|vvv Increase the verbosity of messages
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.

Available commands:
  execute    Execute a single migration version up or down manually.
  generate   Generate a blank migration class.
  help       Displays help for a command
  latest     Outputs the latest version number
  list       Lists commands
  migrate    Execute a migration to a specified version or the latest available version.
  status     View the status of a set of migrations.
  version    Manually add and delete migration versions from the version table.

Creating a migration

run php bin/migrations.php generate and a new migration class will be created in the migrations directory. It has two methods: up() and down(). Both have an instance of Doctrine\DBAL\Schema\Schema passed and an you can do whatever you like in the function to manipulate your database to its next state. Again, the documentation is helpful!

For instance:

<?php

namespace Migrations;

use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;

class Version20141027161210 extends AbstractMigration
{
    public function up(Schema $schema)
    {
        $myTable = $schema->createTable('artists');
        $myTable->addColumn('id', 'integer',
            ['unsigned' => true, 'autoincrement'=>true]);
        $myTable->addColumn('name', 'string', ['length' => 60]);
        $myTable->setPrimaryKey(['id']);
    }

    public function down(Schema $schema)
    {
        $schema->dropTable('artists');
    }
}

Running the migration

Simply run php bin/migrations.php migrate and Migrations will the up() method for all migration classes that haven’t be run yet. In this case it will create a table called artists and update the migration_versions table with the version number of ‘20141027161210’.

To back out of a migration, simple run php bin/migrations.php migration {version number} and Migrations will run the appropriate down() (or up()) methods to get to that version number.

Finally you can run php bin/migrations.php status to find out the current state.

On the whole, it turns out that it’s quite easy to use Migrations as a stand-alone tool outside of Doctrine ORM or Symfony!

2 thoughts on “Using Doctrine Migrations as a standalone tool

Comments are closed.