Doctrine Migrations — Complete Guide to Database Schema Versioning
In this tutorial, you will learn about Doctrine Migrations. We cover key concepts, practical examples, and best practices to help you master this topic.
Doctrine Migrations provide version control for database schemas, allowing teams to generate Migration classes from entity changes and apply them across environments.
What You'll Learn
By the end of this tutorial, you'll set up Doctrine Migrations, generate migrations from entity diffs, write custom SQL migrations, manage versions, and deploy safely.
Why Migrations Matter
Database schema changes must be versioned, reversible, and consistently applied across development, staging, and production. Migrations ensure all environments stay in sync with code.
Real-World Use
A development team adds a new entity for Ratings. Running vendor/bin/doctrine migrations:diff generates the migration. After code review, the migration is applied on staging and production.
Migrations Path
flowchart LR
A[Doctrine ORM] --> B[Doctrine Migrations]
B --> C[Generate Diff]
B --> D[Apply Migration]
B --> E[Rollback]
B --> F[Version Control]
B --> G{You Are Here}
style G fill:#f90,color:#fff
Setup and Configuration
Install and configure Doctrine Migrations.
<?php
// config/migrations.php
use Doctrine\Migrations\DependencyFactory;
use Doctrine\Migrations\Configuration\Migration\PhpFile;
use Doctrine\Migrations\Configuration\EntityManager\ExistingEntityManager;
$config = new PhpFile("migrations.php");
return DependencyFactory::fromEntityManager($config, new ExistingEntityManager($entityManager));
# migrations.php
migrations_paths:
'DoctrineMigrations': 'lib/DoctrineMigrations'
default_environment: 'dev'
table_name: 'doctrine_migration_versions'
organize_migrations: BY_YEAR_AND_MONTH
Generating Migrations
Auto-generate migrations from entity changes.
vendor/bin/doctrine migrations:diff
Generated Migration
The generated migration class.
<?php
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260628120000 extends AbstractMigration {
public function getDescription(): string {
return "Create ratings table";
}
public function up(Schema $schema): void {
$this->addSql("CREATE TABLE ratings (
id INT AUTO_INCREMENT NOT NULL,
product_id INT NOT NULL,
user_id INT NOT NULL,
score INT NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$this->addSql("ALTER TABLE ratings ADD CONSTRAINT FK_RATINGS_PRODUCT FOREIGN KEY (product_id) REFERENCES products(id)");
}
public function down(Schema $schema): void {
$this->addSql("DROP TABLE ratings");
}
}
Custom SQL Migrations
Write migrations with custom SQL for complex changes.
<?php
final class Version20260628130000 extends AbstractMigration {
public function up(Schema $schema): void {
$this->addSql("UPDATE products SET price = price * 1.1 WHERE category_id = (SELECT id FROM categories WHERE name = 'premium')");
$this->addSql("INSERT INTO audit_log (action, table_name) VALUES ('price_update', 'products')");
}
public function down(Schema $schema): void {
$this->addSql("UPDATE products SET price = price / 1.1 WHERE category_id = (SELECT id FROM categories WHERE name = 'premium')");
}
}
Common Mistakes
1. Editing Applied Migrations
Never edit a migration that has been applied to production. Create a new migration for changes.
2. Not Testing Migrations Down
Always test the down() method to ensure rollbacks work correctly.
3. Large Migrations
Break large schema changes into multiple migrations. Each migration should be atomic.
4. Ignoring Migration Dependencies
Use getMigrationDependencies() when migrations must run in a specific order.
5. Not Version-Controlling Migrations
Migrations must be in version control. They are code and should be reviewed.
Practice Questions
1. How do you generate a migration from entity changes?
Run vendor/bin/doctrine migrations:diff to compare the schema with entities.
2. What is the down() method for?
Reverse the changes made in up(), enabling rollback.
3. How do you apply pending migrations?
Run vendor/bin/doctrine migrations:migrate.
4. Can you write raw SQL in migrations?
Yes. Use $this->addSql() in the up() and down() methods.
5. Challenge: Create a migration that adds a column with default data.
<?php
final class Version20260628140000 extends AbstractMigration {
public function up(Schema $schema): void {
$this->addSql("ALTER TABLE users ADD COLUMN timezone VARCHAR(50) NOT NULL DEFAULT 'UTC'");
$this->addSql("UPDATE users SET timezone = 'America/New_York' WHERE region = 'us_east'");
}
public function down(Schema $schema): void {
$this->addSql("ALTER TABLE users DROP COLUMN timezone");
}
}
FAQ
Mini Project: Migration Workflow
Set up a complete migration workflow for a team project.
# Generate migration from entity changes
vendor/bin/doctrine migrations:diff
# Review the generated migration file
code lib/DoctrineMigrations/Version20260628120000.php
# Apply in development
vendor/bin/doctrine migrations:migrate
# Commit both entity changes and migration
git add src/Entity/Rating.php lib/DoctrineMigrations/Version20260628120000.php
git commit -m "Add Rating entity and migration"
# Deploy on production
git pull && vendor/bin/doctrine migrations:migrate --no-interaction
What's Next
Doctrine DQL Doctrine Entities Doctrine Relations
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro