2.3. Timeline

A Timeline is an object comprised of a version collection that’s ordered by version.

2.3.1. Public Methods

The Timeline is in charge of executing one or more migrations in any of four different ways:

upTowards(target)
Executes all migrations in ascending sequential order, starting from the first “pending” version, up to and including the “target” version - and skipping any versions that have already been migrated. Calls each migration’s up() method upon execution.
downTowards(target)
Executes all migrations in descending sequential order, starting from the last migration and down until and including the “target” version. Calls each migration’s down() method upon execution.
goTowards(target)
Combines the previous two operations into one. Executes all migrations up() to and including the “target” version. And then executes all remaining versions down(), starting from the last available version and ending in the version immediately after the “target” version. Useful to make sure all versions before and including “target” are migrated, while all others are pending.
runSingle(version, options)
Runs a single version’s migration using the specified options. Can be called directly and useful mostly for testing purposes in a controlled environment.

2.3.2. Constructor

When being instantiated the Timeline requires a version collection and a comparator as arguments.

VersionCollection $versions

The version collection must contain at least one Delta. All versions in the collection must have a migration assigned to them or otherwise an exception will be thrown.

Something to note is that the VersionCollection object will be cloned before being stored in the Timeline. Since the Timeline doesn’t have any public methods available to access the internal version collection this effectively means that once the Timeline is created its collection cannot be altered.

callable $comparator (optional)

The comparator must be a callable that receives two Versions as arguments and returns a number less than, equal to, or greater than zero (0) if the first version should be executed before than, is the same as, or should be executed after the second version (respectively).

The default comparator simply extracts the first number from each version and subtracts the second one from the first one. See class Delta\Comparator\DefaultComparator for the source code.

MigrationBus $migrationBus (optional)

The MigrationBus (colloquially “bus”) is an object that can handle a MigrationCommand (“command”). The bus consists if a series of AbstractMiddleware (middleware), at least one of which must be a MigrationHandler (handler). Each middleware object receives the command, handles it (e.g. logs some info), and then calls the next middleware. This goes on until the command arrives at the handler (which is also a middleware), who calls the up() or down() function in the command’s migration and ends the chain.

A default MigrationBus is automatically created if none is specified.

2.3.3. Events

Events are an useful to tap into the migration behaviour quickly and cleanly. The Timeline is coupled with a TimelineEmitter class that fires domain events for each operation if an EventDispatcher is present. Events that will be fired are:

Before Collection
DomainEventInterface::COLLECTION_BEFORE
Fired before the Timeline executes one of the collection methods (upTowards, downTowards, goTowards). The listener callback will receive a CollectionEvent object as a parameter, which includes information about the collection of versions, the Options and the target version.
After Collection
DomainEventInterface::COLLECTION_AFTER
Same as above, but fired once the the collection method has finished executing all scheduled migrations.
Before Migration
DomainEventInterface::MIGRATION_BEFORE
Fired before the Timeline executes a single migration, which occurs both as part of executing runSingle and also for each of the migrations executed by any collection method.
After Migration
DomainEventInterface::MIGRATION_AFTER
Same as above, but fired once the migration has finished.

Baleen uses Symfony’s Event Dispatcher component, so listening to timeline events is very easy:

<?php
use Baleen\Migrations\Event;

$dispatcher = $timeline->getEventDispatcher();

$dispatcher->addListener(
    DomainEventInterface::COLLECTION_BEFORE,
    function ($event, $name) {
        // do something
    }
);
$dispatcher->addListener( /* ... */ );

You can also inject your own dispatcher (useful for shared listeners e.g. across different Timeline instances):

<?php
use Symfony\Component\EventDispatcher;

$dispatcher = new EventDispatcher();
$dispatcher->addListener( /* ... */ );
$dispatcher->addListener( /* ... */ );

$timeline->setEventDispatcher($dispatcher);

2.3.4. Custom MigrationBus

As indicated in the Timeline Constructor section, a Timeline can receive a MigrationBus as an optional parameter. The MigrationBus is simply a specialised CommandBus that helps provide strong typing for the MigrateCommand in PHP 5. Its currently powered by the Tactician command bus library.

The Middleware attached to that bus will be used to execute each individual migration, which means behaviors can easily be customised if needed.

The default migration bus and its middleware stack is created by MigrationBusFactory - its very simple:

<?php
new MigrationBus([
    // injects Options into the migration
    new SetOptionsMiddleware(),
    // wraps each migration into a transaction (most commonly a DB transaction)
    new TransactionMiddleware(),
    // in charge of executing the MigrateCommand
    new MigrateHandler(),
]);

You can override the default MigrationBus by simply passing a different instance as a parameter for the Timeline’s constructor.

Creating your own middleware is easy too: just create a new class that extends AbstractMiddleware and add it to the chain when you create the MigrationBus. See any of the default middleware classes for an example of what middleware can do.