A new way to squash your Doctrine migrations

As time flies, Doctrine migrations stacks. After many years of working on a project, you’ll have a high number of migrations. Even if it’s not a big issue, It takes a lot of space in your code sources, and it can take some time in your CI (Continuous Integration).

Doctrine already supports squashing migration, with a roll-up command, but we don’t really like it since it requires us to execute a special command during the deployment.

There are already some blog posts about squashing migrations, but we would like to introduce a new way, we hope, simpler.

A way that does not involve running a new command in production.

In this article, we’ll not cover the why, since the former blog posts do that very well. Let’s focus on the “how”!

Section intitulée how-to-squash-doctrine-migrationsHow to squash Doctrine migrations?

Here is the method proposal:

  1. delete all your migrations files;
  2. execute the following command:
    bin/console doctrine:migrations:dump-schema
  3. Prepend to this migration the following code in the up() method:
    // Replace the `customer` table name by one table that exists in your production database
    if ($this->sm->tablesExist(['customer'])) {
        $this->addSql('DELETE FROM migration_versions');
    // All other code of the migration
  4. Rename the migration file to Version00000000000000.php and rename the class too.

You may want some explanations about this code 👀

The first line checks if the table customer exists. If it does, it means that the previous migrations have already been executed: it’s the (pre)production database. If it’s the case, the second line deletes all the migration versions, so we can restart from scratch. The last line returns, so we don’t execute the rest of the migration.

If the table does not exist, it means that we are in a fresh database (dev, ci), so we can execute the migrations.

At the end, whatever the case, we have a migration that will be executed only once, and that will delete all the migration versions. So we start from scratch. The migration table will contain only this very first migration.

You may wonder why this naming? Version contains a timestamp. The number zero is here to be sure that this migration will be executed first. So if anyone has opened a pull-request before you, you’ll be sure that your migration will be executed first, and the others one after.

Section intitulée how-to-use-it-in-your-projectHow to use it in your project?

There is nothing special to do in development, in preprod, nor in prod, etc! that’s the point 🎉

Run your migrations as usual.

Section intitulée a-few-more-wordsA few more words

If in your previous migrations you had some INSERT statements, you may want to re-add them. Your CI may need them. But if your fixtures are well written, this is not needed. It depends on your code.

After doing that, don’t forget to validate your mapping and schema:

bin/console doctrine:schema:validate

If the schema is not valid, Doctrine may have missed some schema generation. If it’s the case, create a new migration:

bin/console doctrine:migration:diff

Then, merge this migration into the Version00000000000000 migration, and drop the new file.

What do you think about this tip? Let us know in the comments!

⚠ This does not work well with third party bundles which also ships migration! Be cautious!

Commentaires et discussions

Nos formations sur ce sujet

Notre expertise est aussi disponible sous forme de formations professionnelles !

Voir toutes nos formations

Ces clients ont profité de notre expertise