<< Doctrine 1.0.3 Released! The Bleeding Edge: Website Upgraded >>

New to Migrations in 1.1

New to Migrations in 1.1

Posted by jwage 3 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.

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-models

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

1224273485_add_user.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
/**
 * 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
/**
 * 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
/**
 * 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 (7) [ add comment ]

This makes my day ! - Posted by jukea about 3 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 3 months ago.

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

Thanks - Posted by Vincent about 3 months ago.

This sounds very useful, thanks!

Posted by Markus Staab about 3 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 3 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 2 months ago.

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

symfony - Posted by Matthias about about 1 month 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..)

Create Comment
Enter 'follow the doctrine' without the quotes