New to Migrations in 1.1

Posted by jwage 9 months ago

In Doctrine 1.1 migrations are much easier to work with. In addition to the increased stability we have enhanced the migrations in a few ways to simplify the use of them dramatically.

We have introduced the following features.

  • Diff Tool - Generate migration classes by simply changing your schema and generating the migrations.
  • Down Automation - Doctrine now has the ability to automate the down of of a migration call.

Generating Migrations

Imagine you have the following starting schema.

---
User:
  columns:
    username: string(255)
    password: string(255)

Build your initial models from the schema.

$ ./doctrine generate-models-yaml

Now we want to enhance our schema to add some new columns as well as a new model with the following schema.

---
User:
  columns:
    username: string(255)
    password: string(255)
    email_address: string(255)

Phonenumber:
  columns:
    user_id: integer
    phonenumber: string(25)
  relations:
    User:
      onDelete: CASCADE
      foreignAlias: Phonenumbers

Now by simply changing our schema we can generate the migrations required to upgrade our database.

$ ./doctrine generate-migrations-diff

Now in your migrations directory you will see 3 migration classes created for you.

1224273485_add_user.php

<?php

<?php
/**
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class AddUser extends Doctrine_Migration_Base
{
  public function up()
  {
    $this->createTable('up', 'user', array('id' => array('type' => 'integer', 'length' => 8, 'autoincrement' => true, 'primary' => true), 'username' => array('type' => 'string', 'length' => 255), 'password' => array('type' => 'string', 'length' => 255)), array('indexes' => array(), 'primary' => array(0 => 'id')));
  }

  public function down()
  {
    $this->dropTable('up', 'user');
  }
}

1224273485_version1.php

<?php

<?php
/**
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class Version1 extends Doctrine_Migration_Base
{
  public function up()
  {
    $this->createTable('up', 'phonenumber', array('id' => array('type' => 'integer', 'length' => 8, 'autoincrement' => true, 'primary' => true), 'user_id' => array('type' => 'integer', 'length' => 8), 'phonenumber' => array('type' => 'string', 'length' => 25)), array('indexes' => array(), 'primary' => array(0 => 'id')));
    $this->addColumn('up', 'user', 'email_address', '255', 'string', array ());
  }

  public function down()
  {
    $this->dropTable('up', 'phonenumber');
    $this->removeColumn('up', 'user', 'email_address');
  }
}

1224273486_version2.php

<?php

<?php
/**
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class Version2 extends Doctrine_Migration_Base
{
  public function up()
  {
    $this->addIndex('up', 'phonenumber', 'phonenumber_user_id_user_id', array('fields' => array(0 => 'user_id')));
    $this->createForeignKey('up', 'phonenumber', array('name' => 'phonenumber_user_id_user_id_idx', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user', 'onUpdate' => NULL, 'onDelete' => 'CASCADE'));
  }

  public function down()
  {
    $this->removeIndex('up', 'phonenumber', 'phonenumber_user_id_user_id', array('fields' => array(0 => 'user_id')));
    $this->dropForeignKey('up', 'phonenumber', array('name' => 'phonenumber_user_id_user_id_idx', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user', 'onUpdate' => NULL, 'onDelete' => 'CASCADE'));
  }
}

Down Automation

In addition to Doctrine being able to generate migrations based on your schema changes, you can now easily automate the down of most methods. The last migration class could be simplified a lot by doing the following.

<?php

<?php
/**
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class Version2 extends Doctrine_Migration_Base
{
  public function migrate($direction)
  {
    $this->addIndex($direction, 'phonenumber', 'phonenumber_user_id_user_id', array('fields' => array(0 => 'user_id')));
    $this->createForeignKey($direction, 'phonenumber', array('name' => 'phonenumber_user_id_user_id_idx', 'local' => 'user_id', 'foreign' => 'id', 'foreignTable' => 'user', 'onUpdate' => NULL, 'onDelete' => 'CASCADE'));
  }
}

Notice that in this example we only have one method named migrate() which receives a direction. Most API methods are easy to automate the opposite down so when migrate is called with $direction = 'down' then the index and foreign key will be dropped instead of added.


Comments (12) [ add comment ]

This makes my day ! Posted by jukea about 9 months ago.

Hi jwage,

The auto generation of migration classes is really a welcome feature ! Thanks for implementing it !

After reading this article, I still had a few questions unanswered thought :

  • migration classes are generated by comparing the schema to the models, or to the database ?

  • what is that number at the beginning of migration classes filenames ?

  • why 3 files when the schema has been updated one time ?

  • why add_user instead of version_0 ?

  • how to actually update the database to reflect the changes to the schema ? I suppose you need

keep up the good work!

oops Posted by jukea about 9 months ago.

to avoid confusion .."i suppose you need" was a leftover from another sentence :)

Thanks Posted by Vincent about 9 months ago.

This sounds very useful, thanks!

Posted by Markus Staab about 9 months ago.

would be nice to have a timestamp/date in the head of the comment of the generated files..

also it would be better to read if the migration statements contain some newlines..

nice work!

@jukea Posted by jwage about 9 months ago.

  • migration classes are generated by comparing the schema to the models, or to the database ?

The migrations can be generated by comparing any two sets of schema information. It is simple a "from" and a "to" and it can be a database, yaml schema files or models and it will compare them and produce a migrations class. The generate-migrations-models task compares the models and the yaml schema file but you can implement your own to do whatever you want.

  • what is that number at the beginning of migration classes filenames ?

It is just a unique timestamp to ensure uniqueness and proper order of the migration classes. This was changed from the 001, 002 for obvious reasons.

  • why 3 files when the schema has been updated one time ?

The generate-migrations-models command builds the migrations to recreate the database if no migration classes exist so first it creates all the migrations to create everything, then the migrations for the differences found between the old models on disk and the modified schema files.

  • why adduser instead of version0 ?

The initial migrations creation of all existing models create one class per model and then a class to create all foreign keys so we can name them appropriately. When we generate a class based on a diff it can contain any number of different changes so the name is a simple incremented version number.

  • how to actually update the database to reflect the changes to the schema ? I suppose you need

You would simply run the migrate command.

$ ./doctrine migrate

And then rebuild your models from the yaml schema files.

$ ./doctrine generate-models-yaml

Brilliant work Posted by David Arthur about 8 months ago.

This is an excellent addition, saves a lot of time and i'd like to say thanks!

symfony Posted by Matthias about 8 months ago.

I suppose this is not yet available for / with a symfony plugin?

What about creating another sfDoctrineExtraPlugin that contains additional tasks/whatever or things that require another Doctrine version. (hm.. also requires that there is a way to substitute the Doctrine version of sfDoctrinePlugin.. that can become a bit difficult..)

Behaviors Posted by Sailor about 2 months ago.

How can I insert behaviors in a model using migrations ?

Thank's in advance !

Behaviors Posted by Sailor about 2 months ago.

How can I insert behaviors in a model using migrations ?

Thank's in advance !

Behaviors Posted by Sailor about 2 months ago.

How can I insert behaviors in a model using migrations ?

Thank's in advance !

Are there typos? Posted by benbac about 2 months ago.

has anyone successfully ran the 'generate-migrations-models' task to generate the migrations of the diffs (found between the old models on disk and the modified schema file)?

Shouldn't generate-migrations-diff do what generate-migrations-models is supposed to do?

Is there a typo?

Old post Posted by jwage about 2 months ago.

This post is old and since it was made the task changed. I will update the blog post.

Create Comment