doctrine迁移问题源于实体元数据与数据库结构不一致,需统一id策略、清理冗余序列、校验三态同步、配置ci参数、手写安全down()并严格测试。

doctrine:migrations:diff 生成空迁移或删 ID 自增
这不是命令坏了,是 Doctrine 实体元数据和数据库实际结构对不上。常见于 PostgreSQL 中:@ORM\Column(options={"autoincrement":true}) 完全无效,还会干扰 diff 判断——Doctrine 第二次比对时发现“表有默认值但实体没声明序列”,就认定为冗余,反向生成 ALTER COLUMN id DROP DEFAULT。
更稳妥的做法是改用语义明确的 ID 策略:@ORM\GeneratedValue(strategy="IDENTITY"),并删掉所有 options={"autoincrement":true}。如果已存在旧序列(如 user_id_seq),手动清理或重命名,避免 Doctrine 二次猜错。
- PHP 8.2+ 下,
strategy="SEQUENCE"必须配sequenceName,否则 Doctrine 首次建序列、第二次误判“无声明”,触发危险移除 - Symfony 7.2 默认启用严格平台校验,对
GENERATED BY DEFAULT AS IDENTITY识别更准,反而暴露旧配置模糊性 - 运行
php bin/console doctrine:schema:update --dump-sql对比输出,能快速定位元数据是否被正确加载
执行 migrate 时提示 “Table already exists” 或 “Column doesn’t exist”
这不是 SQL 写错了,是 doctrine_migration_versions 表、本地迁移文件、数据库真实结构三者脱节。最常见原因是:你用 psql 或 MySQL CLI 手动改过表,但没同步更新状态表;或者多人协作时,别人合入了迁移文件却没执行,你本地 status 显示 “Not executed”,而库中已有对应结构。
Symfony 7.2 默认开启 transactional: true,但如果某个 up() 方法里混写了 $this->addSql() 和 $schema->createTable(),事务边界可能异常,导致部分语句生效、部分回滚,残留半截状态。
- 先跑
php bin/console doctrine:migrations:status,确认 “Executed migrations” 列表与数据库实际结构一致 - 若不一致,可用
php bin/console doctrine:migrations:version --add 'DoctrineMigrations\Version20240515123456'手动标记某版本为已执行(慎用) - 生产环境务必禁用
doctrine:schema:update --force,它绕过迁移机制,直接写库,会彻底破坏版本一致性
CI/CD 中 migrate 命令卡住或退出码异常
默认行为下,doctrine:migrations:migrate 在无新迁移时返回退出码 1(失败),CI 流水线直接中断;同时交互式确认也会卡住,除非显式关闭。
别指望加个 --no-interaction 就万事大吉——它只解决卡住问题,不解决“无迁移时失败”的逻辑缺陷。
- 必须加
--no-interaction关闭提示 - 必须加
--allow-no-migration让无新迁移时返回 0(成功) - 生产环境建议加
--timeout=300,防大表锁表超时被 kill - 迁移中禁止执行耗时 PHP 逻辑(如循环处理百万行),应拆成独立命令或异步任务
回滚失败或 down() 不生效
Doctrine 从不自动生成安全的 down(),它只是把 up() 里写的 $schema->addColumn() 反向拼成 dropColumn(),但对复杂场景完全不可靠。比如 up() 里加了唯一索引,down() 却没删索引;或者字段在上一次迁移中已被删,这次 down() 还试图删一次,直接报错。
真正要命的是:破坏性操作(如 DROP COLUMN、类型收缩)一旦执行 down(),数据就永久丢失,Doctrine 不备份、不校验、不提醒。
-
down()必须手写,且和up()严格对称——不是“逆向生成”,而是“人工设计可逆路径” - 涉及数据变更(如 INSERT/UPDATE)的迁移,
down()必须提供对应反向操作,不能留空 - 测试回滚前,先用
--dry-run查看将执行的 SQL,再在开发库实测 - 跨多版本回滚(如从 v20260701 → v20260501)前,务必整库备份——中间任意一个
down()失败,整个回滚链就断了











