Using Doctrine Migrations to Manage Schema Changes in PHP Projects
Learn how to set up, create, run, and roll back Doctrine Migrations for reliable database schema management. Includes a concrete example, configuration snippets, and verification steps.
22 Jun 2026, 00:27 UTC

Desired Outcome
The goal is to keep the database schema in sync with your PHP entities while preserving data integrity. Doctrine Migrations provides a repeatable, versioned approach to apply, verify, and roll back schema changes safely.
Prerequisites
- Composer installed and a
composer.jsonthat requiresdoctrine/ormanddoctrine/migrations. - A working Doctrine ORM configuration with a
Connectionpointing to a database that supportsALTERandDROPoperations (e.g., MySQL, PostgreSQL). - Database credentials with
ALTERrights. Use a separate test database for development and staging. - PHP 8.1+ (adjust based on your project's PHP version).
Configuring Doctrine Migrations
Doctrine Migrations reads a YAML configuration file, typically located at config/doctrine_migrations.yaml. The file defines where migration classes live, which database connection to use, and other settings.
doctrine_migrations:
migrations_paths:
# Namespace for migration classes
'Doctrine\Migrations': '%kernel.project_dir%/migrations'
storage:
# Table that tracks applied migrations
table_name: migration_versions
# Optional: specify the connection name if you have multiple connections
connection: default
Place the file in the root of your project or adjust the path in config/packages/doctrine.yaml so Symfony picks it up.
Creating a Migration
After altering your entity classes, generate a migration that reflects those changes. The diff command compares the current database schema to the entity metadata and writes an up() and down() method.
# Run from the project root
php bin/console doctrine:migrations:diff
Typical output:
[OK] Generated 1 migration(s).
- Doctrine\Migrations\Version20261017193500
The generated file will look like:
false]);
$table->addIndex(['email_verified']);
}
public function down(Schema $schema): void
{
$table = $schema->getTable('user');
$table->dropColumn('email_verified');
}
}
Verify that the down() method reverses every change in up(). If the migration drops data or alters columns, double‑check that the rollback will not corrupt existing data.
Applying Migrations
Run pending migrations in the order they were generated. The command updates the migration_versions table and executes the up() method for each migration.
php bin/console doctrine:migrations:migrate
During execution, Doctrine shows the migration version and a progress bar. After completion, you should see a confirmation line like:
[OK] Migrated to version 20261017193500.
At this point the database schema reflects the new column, and the migration table records the applied version.
Verifying the Result
- Check the migration table: Ensure the new version appears.
SELECT * FROM migration_versions; -- should list 20261017193500 - Inspect the table structure: Use a database client or
DESCRIBE user;to confirm theemail_verifiedcolumn exists with the correct type and default. - Query data integrity: Run a sample SELECT to ensure existing rows are unaffected.
Rolling Back
If the migration introduced bugs or needs adjustment, roll it back. Doctrine Migrations rolls back the most recent batch by default.
php bin/console doctrine:migrations:rollback
After rollback, the migration_versions table will no longer contain the last applied version, and the email_verified column should be absent.
Recovery Options
- Database backup: Always back up before running migrations on production. Restoring a backup is the quickest recovery if a migration fails.
- Dry run: Use
--dry-runto preview SQL without executing.php bin/console doctrine:migrations:migrate --dry-run - Split large migrations: For complex changes that touch many tables, split into multiple smaller migrations to reduce lock time.
Summary
Doctrine Migrations turns schema evolution into a versioned, reversible process. By following the steps above—configuring, generating, applying, verifying, and rolling back—you can safely evolve your database while keeping data integrity intact. Always test migrations on a non‑production copy and keep backups before applying destructive changes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.