Symfony Migration 与版本管理详解
基础概念
Symfony 的迁移系统(DoctrineMigrationsBundle)用于管理数据库 schema 的版本变更,类似于 Git 管理代码版本。

安装与配置
# 安装迁移包
composer require doctrine/doctrine-migrations-bundle
# 配置文件 (config/packages/doctrine_migrations.yaml)
doctrine_migrations:
migrations_paths:
'App\Migrations': '%kernel.project_dir%/migrations'
storages:
table_storage:
table_name: 'migration_versions'
迁移命令大全
# 生成新的迁移(基于实体差异) php bin/console make:migration # 生成空迁移(自定义 SQL) php bin/console make:migration --empty # 执行迁移 php bin/console doctrine:migrations:migrate # 回滚到指定版本 php bin/console doctrine:migrations:migrate App\Migrations\Version20210101120000 # 查看状态 php bin/console doctrine:migrations:status # 查看列表 php bin/console doctrine:migrations:list # 生成迁移的 SQL 语句(不执行) php bin/console doctrine:migrations:dump-schema
迁移文件结构
// migrations/Version20230101000000.php
declare(strict_types=1);
namespace App\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230101000000 extends AbstractMigration
{
public function getDescription(): string
{
return '创建用户表';
}
public function up(Schema $schema): void
{
// 方法1: 使用 Schema builder
$table = $schema->createTable('users');
$table->addColumn('id', 'integer', ['autoincrement' => true]);
$table->addColumn('username', 'string', ['length' => 100]);
$table->addColumn('email', 'string', ['length' => 255]);
$table->setPrimaryKey(['id']);
// 方法2: 直接 SQL
$this->addSql('CREATE TABLE users (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
}
public function down(Schema $schema): void
{
// 回滚操作
$schema->dropTable('users');
// 或者
$this->addSql('DROP TABLE users');
}
}
高级特性
// 1. 条件执行
public function isTransactional(): bool
{
return false; // 不使用事务
}
// 2. 预检查
public function preUp(Schema $schema): void
{
// 执行前的检查
}
// 3. 使用 Platform 特定 SQL
public function up(Schema $schema): void
{
$platform = $this->connection->getDatabasePlatform();
if ($platform instanceof MySqlPlatform) {
$this->addSql('ALTER TABLE users ADD INDEX idx_username (username)');
}
}
// 4. 使用参数绑定
public function up(Schema $schema): void
{
$this->addSql('UPDATE users SET username = :name WHERE id = :id', [
'name' => 'admin',
'id' => 1
]);
}
版本管理最佳实践
1 命名规范
# 推荐命名:YYYYMMDDHHMMSS_描述
Version20230101120000_CreateUserTable.php
Version20230101130000_AddEmailToUser.php
Version20230101140000_CreateProductTable.php
2 工作流程
# 开发环境 1. 修改 Entity 2. 生成迁移:make:migration 3. 检查生成的 SQL 4. 执行迁移:migrate 5. 提交到版本控制 # 生产环境 1. 拉取代码 2. 执行迁移:migrations:migrate 3. 验证数据库状态
3 回滚策略
# 回滚到指定版本 php bin/console doctrine:migrations:migrate App\Migrations\Version20230101000000 # 查看迁移历史 php bin/console doctrine:migrations:latest # 执行下一个迁移 php bin/console doctrine:migrations:execute App\Migrations\Version20230101000000 --up # 撤销单个迁移 php bin/console doctrine:migrations:execute App\Migrations\Version20230101000000 --down
配置文件高级选项
# config/packages/doctrine_migrations.yaml
doctrine_migrations:
table_storage:
table_name: 'migration_versions'
version_column_name: 'version'
version_column_length: 1024
executed_at_column_name: 'executed_at'
execution_time_column_name: 'execution_time'
# 组织迁移目录
migrations_paths:
'App\Migrations\App': '%kernel.project_dir%/migrations/app'
'App\Migrations\Data': '%kernel.project_dir%/migrations/data'
# 格式化
organize_migrations: 'year_and_month' # 或 'year'
custom_template: '%kernel.project_dir%/migrations/template.tpl'
多环境配置
# config/packages/dev/doctrine_migrations.yaml
doctrine_migrations:
# 开发环境自动生成迁移
auto_generate: true
# config/packages/prod/doctrine_migrations.yaml
doctrine_migrations:
# 生产环境禁用自动生成
auto_generate: false
团队协作指南
# 1. 创建特性分支 git checkout -b feature/add-user-profile # 2. 修改实体,生成迁移 php bin/console make:migration # 3. 查看迁移内容 php bin/console doctrine:migrations:up-to-date # 4. 合并到主分支前处理冲突 # 如果多个迁移有相同的时间戳,手动修改文件名 # 5. 在测试/生产环境执行 php bin/console doctrine:migrations:migrate --env=prod
常见问题与解决方案
// 问题1: 迁移文件已提交,但需要修改
// 解决:创建新的迁移文件,不要修改已提交的迁移
// 问题2: 迁移执行失败
public function up(Schema $schema): void
{
try {
// 迁移逻辑
} catch (\Exception $e) {
// 错误处理
$this->write('迁移执行失败: ' . $e->getMessage());
}
}
// 问题3: 数据迁移
public function up(Schema $schema): void
{
// 先改表结构
$this->addSql('ALTER TABLE users ADD COLUMN status VARCHAR(20)');
// 再更新数据
$this->addSql('UPDATE users SET status = :status', [
'status' => 'active'
]);
}
监控与审计
# 查看迁移日志 php bin/console doctrine:migrations:status php bin/console doctrine:migrations:latest # 跟踪迁移执行时间 # 在 migration_versions 表中有 execution_time 字段
CI/CD 集成
# .gitlab-ci.yml 或 Jenkinsfile
deploy:
script:
- composer install --no-dev
- php bin/console doctrine:migrations:migrate --no-interaction
- php bin/console cache:clear
- 每次数据库变更都使用迁移,不要手动修改数据库
- 迁移文件要提交到版本控制,确保团队同步
- 每个迁移只做一件事,便于回滚和审计
- 始终提供
down()方法,确保可回滚 - 在生产环境前测试迁移,避免数据丢失
- 使用事务确保数据一致性,复杂迁移可能需要关闭自动事务
- 数据库 schema 和代码版本要匹配,迁移执行前更新代码
这样就能确保数据库变更像代码变更一样可追踪、可回滚、可协作。