2.2. Migration

A migration in Baleen is a class that implements MigrationInterface. It can optionally implement one or more additional interfaces to describe special capabilities.

2.2.1. MigrationInterface

All migration classes must implement MigrationInterface, which declares the up() and down() methods. Each of those methods should modify the target resource symmetrically:

  • UP: Describes what happens when the resource moves up (or forwards) in the Timeline.
  • DOWN: Describes how to revert / rollback the resource if up has already been executed.

The following snippet illustrates a simple migration that creates (or destroys) a table. Note how the up and down methods are perfectly symmetrical to each other:

<?php
class v001_AddHireDateToStaff implements MigrationInterface
{

    // a constructor would go here to set up the connection

    /**
      * Adds a "hire_date" column to the staff table.
      */
    public function up() {
        $this->connection->exec("ALTER TABLE staff ADD hire_date date");
    }

    /**
      * Removes the "hire_date" column from the staff table.
      */
    public function down() {
        $this->connection->exec("ALTER TABLE staff REMOVE hire_date date");
    }
}

2.2.2. Additional Capabilities

Additional interfaces can be used to indicate special features available for a certain migration. Baleen supplies a few common interfaces for commonly-used features, but more can be created to suit specific needs.

2.2.2.1. OptionsAwareInterface

A migration that implements this interface must receive an Options object after being instantiated. The Options object will include contextual information that could be useful for the migration. Refer to the Options class documentation for more information.

2.2.2.2. TransactionAwareInterface

A migration that implements this interface must implement a set of methods that will aid in generating transactions. Useful for example to wrap up() or down() commands into database transactions. The methods declared in this interface are:

  • begin(): called right before executing the migration (i.e. the up() or down() method).
  • finish(): called right after the migration finished executing without any exceptions.
  • abort(Exception): called if any exception is fired during the execution of the migration. The exception is passed as the only parameter. The abort method must recover the resource from the exception (e.g. roll-back the transaction) and optionally re-throw the exception if needed.

2.2.2.3. Other Capabilities

Additional interfaces can be specified to deal with special features required by any particular migration process. In order to make this easy to customise, migrations are run through a configurable Command Bus pattern.

2.2.3. Custom Migrations

Creating specialised Migration classes is very easy: all that’s needed is a migration class that end-users must implement in their migrations.

If your abstract migration class has dependencies that need to be injected through its constructor then simply create a Migration\Factory\FactoryInterface instance and pass it to the VersionRepository:

<?php

$factory = new MyMigrationFactory(/** dependencies here **/); // instance of MigrationFactory
$repository->setMigrationFactory($factory);
// $repository->fetchAll() will call the factory to instantiate each Migration

The following section is an example of how an abstract PDO migration class can power database migrations.

2.2.4. Example

For the purposes of this example, imagine the following classes are each located on a separate file under a folder in your project called ./migrations.

The first file declares a sample abstract class to incorporate common functionality. It is of course up to the user whether something like this is really required or not.

File ./migrations/AbstractPDOMigration.php:

<?php
use Baleen\Migration\MigrationInterface;
use Baleen\Migration\Capabilities;
use Baleen\Migration\MigrationInterface;
use Baleen\Migration\RunOptions;

/**
 * You can be as creative as you want here. The only requirement is to implement
 * MigrationInterface.
 */
class AbstractPDOMigration
    implements MigrationInterface,
               Capabilities/OptionsAwareInterface,
               Capabilities/TransactionAwareInterface
{
    /** @var PDO **/
    protected $connection; // gets initialised in the constructor

    /** @var RunOptions **/
    protected $options;

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

    public function begin() {
        $this->connection->beginTransaction();
    }

    public function finish() {
        $this->connection->commit();
    }

    public function abort() {
        $this->connection->rollBack();
    }

    public function setOptions(RunOptions $options) {
        $this->options = $options;
    }
}

And now the concrete migrations:

File ./migrations/v001_AddHireDateToStaff.php:

<?php
class v001_AddHireDateToStaff extends AbstractPDOMigration
{
    public function up() {
        $this->connection->exec("ALTER TABLE staff ADD hire_date date");
    }

    public function down() {
        $this->connection->exec("ALTER TABLE staff REMOVE hire_date date");
    }
}

File ./migrations/v002_SeedJoeBloggs.php:

<?php
class v002_SeedJoeBloggs extends AbstractPDOMigration
{
    public function up() {
        $this->connection->exec(
            "INSERT INTO staff (id, first, last) VALUES (23, 'Joe', 'Bloggs')"
        );
    }

    public function down() {
        $this->connection->exec("DELETE FROM staff WHERE id = 23");
    }
}

// ... etc - for the purposes of this example imagine there are 100 migrations