symfony数据库迁移完整流程:先用make:entity定义实体并加注解,再运行doctrine:migrations:diff生成带时间戳的迁移文件,最后执行doctrine:migrations:migrate建表;初始数据应写入up()方法的addsql中,down()需对应删除,全程通过doctrine_migration_versions表版本控制。

在 Symfony 项目中完成从零建表到数据填充的完整数据库迁移,核心是分三步走:先定义实体结构,再生成并执行结构迁移,最后用迁移文件安全插入初始数据。整个过程依赖 Doctrine Migrations,不是临时脚本,而是可追溯、可协作、可上线的正式流程。
建表:改实体 → 生成迁移 → 执行
不要手动写 CREATE TABLE,也不要用 doctrine:schema:create 一次性建库——这会绕过版本控制,无法回滚、无法协同。
- 用命令创建实体:
php bin/console make:entity User,按提示添加字段(如email字符串、isActive布尔) - 确保每个属性都有完整注解,例如:
/** @ORM\Column(type="string", length=255) */private string $email; - 运行差异比对:
php bin/console doctrine:migrations:diff,自动生成类似Version20260601123000.php的文件 - 检查生成文件中的
up()方法,确认 SQL 符合目标数据库(比如 MySQL 不支持UUID类型直接写法,需用guid或字符串模拟) - 执行迁移:
php bin/console doctrine:migrations:migrate
填数据:在迁移里写 insert,不靠 fixtures
如果只是插入固定配置项(如默认角色、系统参数、国家列表),应把 INSERT 写进迁移文件,而不是用 DoctrineFixturesBundle——后者适合开发/测试数据,不适合生产环境的“结构级”初始数据。
- 打开刚生成的迁移文件,在
up()方法末尾追加:$this->addSql("INSERT INTO user (email, is_active) VALUES ('admin@example.com', 1)"); - 注意:SQL 必须兼容目标数据库方言(避免使用
ON DUPLICATE KEY UPDATE等 MySQL 特有语法,除非明确只跑 MySQL) - 若要保证幂等(多次执行不报错),可用带条件的插入,例如:
$this->addSql("INSERT IGNORE INTO user (email, is_active) VALUES ('admin@example.com', 1)");
或用 Doctrine 的平台抽象:$platform = $schema->getPlatform();<br>if ($platform instanceof MySqlPlatform) { ... }
回滚与安全边界
填入的数据属于“结构附带数据”,必须能随表一起撤回。因此 down() 方法不能留空。
- 手动补全
down():删表时自动清数据;若只删记录,就写对应DELETEpublic function down(Schema $schema): void {<br> $this->addSql("DELETE FROM user WHERE email = 'admin@example.com'");<br> // 紧接着 dropTable 或 dropColumn<br>} - 禁止在
up()中执行不可逆操作,例如:DROP TABLE、ALTER TABLE ... DROP COLUMN、收缩字段长度——这类变更应拆成两步迁移(先加字段+迁数据,再删旧字段) - 生产环境执行前,务必运行:
php bin/console doctrine:migrations:status确认待执行列表,并人工核对迁移文件内容
多环境一致性保障
本地建的表和数据,要确保在 CI、Staging、Production 环境效果一致。
- 所有迁移文件必须提交进 Git,且命名含时间戳(Doctrine 自动生成),避免合并冲突
- CI 流水线中禁止自动执行 migrate 到 production;只允许在 staging 自动运行,production 必须由 DBA 手动触发
- 不同数据库类型(MySQL / PostgreSQL)下,迁移文件应通过
$schema->getPlatform()分支处理,或统一用 Doctrine 抽象方法(如$table->addColumn('id', 'integer')->setAutoincrement(true))代替原始 SQL











