唯一推荐的迁移生成方式是先修改实体再执行doctrine:migrations:diff;手动编写version*.php易导致索引遗漏、跨库不兼容、幂等性破坏及后续diff误判。

别手写迁移文件,先改实体再执行 doctrine:migrations:diff —— 这是唯一推荐的生成方式,其他路径(比如直接写 SQL 或用过时命令)大概率导致状态错乱或不可回滚。
为什么不能手动写 Version*.php 文件
Doctrine 迁移依赖实体类与数据库结构的精确比对。手动写 up() 和 down() 容易漏掉索引、约束、外键顺序、字段默认值等细节;更危险的是,它绕过了 Doctrine 的差异检测逻辑,后续再运行 doctrine:migrations:diff 会误判“还有变更没提交”,造成重复 ADD COLUMN 或 ALTER 冲突。
- 实体里加了
@ORM\Column(type="string", length=128),但迁移里忘了设length→ MySQL 可能截断数据 - 用了
$this->addSql('ALTER TABLE user CHANGE name full_name VARCHAR(255)')→ PostgreSQL 不支持CHANGE,直接报错 - 在
up()里调用$schema->dropTable('old_log')→ 下次 diff 会认为“该表本不该存在”,生成反向 CREATE,破坏幂等性
标准生成流程:改实体 → diff → 检查文件
生成不是一步命令的事,关键在前后验证。执行前确保:
- 实体类已更新,且包含完整注解:
@ORM\Entity、@ORM\Table、字段级@ORM\Column等,缺一个都可能让diff无感知 - 数据库是当前主干(main)对应的 schema —— 切到 clean
main分支,用php bin/console doctrine:schema:validate确认“Mapping is correct”且“No pending migrations” - 运行
php bin/console doctrine:migrations:diff,它会在migrations/下生成类似Version20260901160500.php的文件 - 立刻打开新文件,检查:
-
up()里是否只含$schema->createTable()、$table->addColumn()等安全操作 - 有没有意外出现的
$this->addSql()—— 如果有,确认它是否跨数据库兼容(比如 UUID 字段在 MySQL 和 PG 写法不同) -
down()是否只是简单逆向(如dropTable),而不是删字段+改类型这种不可逆动作
-
常见失败原因和对应操作
如果 doctrine:migrations:diff 没生成任何内容,或提示 “No changes detected”,别急着重试,先排查这些硬性条件:
-
doctrine:generate:entities是 Symfony 5.4+ 已弃用的命令,用了会导致实体元数据不被识别 → 彻底删除生成的*.orm.yml或*.xml映射文件,只保留 PHP 注解 - 多数据库场景下,默认只扫描
defaultEntityManager → 加--em=customer_manager指定管理器 - 实体类命名空间错误(如写成
AppEntityUser而非App\Entity\User)→doctrine:mapping:import会失败,diff也找不到映射 - 本地数据库比代码“超前”:比如你刚手动跑了
doctrine:schema:update --force,而实体还没补字段 → 先 revert 手动变更,或同步实体后再 diff
真正容易被忽略的点是时间戳和分支一致性:生成迁移的那一刻,你的实体必须代表「即将合入 main 的最终形态」,而数据库必须是「main 当前已部署的形态」。差一个 commit,生成的迁移就可能在 CI 上炸库。











