# 数据迁移操作

<cite>
**本文引用的文件**
- [AGENTS.MD](file://AGENTS.MD)
- [20230320162718_migration_cmf_role_type.php](file://vendor/thinkcmf/cmf/src/data/migrations/20230320162718_migration_cmf_role_type.php)
- [20230322153218_migration_cmf_admin_api.php](file://vendor/thinkcmf/cmf/src/data/migrations/20230322153218_migration_cmf_admin_api.php)
- [20231010004518_migration_cmf_theme_file_i18n.php](file://vendor/thinkcmf/cmf/src/data/migrations/20231010004518_migration_cmf_theme_file_i18n.php)
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)
- [composer.lock](file://composer.lock)
- [Cli.php](file://vendor/thinkcmf/cmf/src/console/command/Cli.php)
</cite>

## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 引言
本文件面向 ThinkCMF 的数据迁移操作，系统性阐述如何在迁移中进行数据的插入、更新、删除与查询，并结合仓库中的迁移样例与底层迁移框架，给出最佳实践、性能优化建议、常见问题与解决方案，以及可复用的复杂场景处理思路。读者无需深入数据库即可理解迁移流程与数据操作要点。

## 项目结构
ThinkCMF 使用 xia/migration 提供的 Phinx 迁移能力，迁移文件位于 vendor/thinkcmf/cmf/src/data/migrations 下，遵循 Phinx 规范。迁移命令通过 ThinkPHP 控制台集成，可在部署或初始化时自动执行。

```mermaid
graph TB
A["ThinkCMF 应用"] --> B["迁移命令入口<br/>php think migrate:*"]
B --> C["迁移文件<br/>vendor/thinkcmf/cmf/src/data/migrations/*.php"]
C --> D["Phinx 抽象迁移类<br/>AbstractMigration"]
D --> E["表对象 Table<br/>定义列/索引/外键/数据"]
E --> F["适配器执行<br/>Adapter.execute/query/saveData"]
```

图表来源
- [AGENTS.MD](file://AGENTS.MD)
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

章节来源
- [AGENTS.MD](file://AGENTS.MD)
- [composer.lock](file://composer.lock)

## 核心组件
- 迁移抽象类：封装迁移生命周期、SQL 执行、查询、表对象创建等能力。
- 表对象：统一管理建表、改表、索引、外键及批量数据写入。
- 迁移文件：具体业务迁移逻辑，包含表结构变更与数据初始化/转换。

章节来源
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

## 架构总览
迁移执行链路从命令行触发，经由 ThinkCMF 控制台，调用迁移框架执行具体迁移文件，最终通过适配器对数据库进行结构与数据操作。

```mermaid
sequenceDiagram
participant Dev as "开发者"
participant CLI as "命令行接口"
participant Mig as "迁移框架"
participant Abs as "抽象迁移类"
participant Tbl as "表对象"
participant DB as "数据库"
Dev->>CLI : 执行迁移命令
CLI->>Mig : 解析并加载迁移文件
Mig->>Abs : 实例化迁移类
Abs->>Tbl : table()/addColumn()/addIndex()/create()/update()
Tbl->>Abs : saveData()/bulkinsert()/insert()
Abs->>DB : execute()/query()/bulkinsert()/insert()
DB-->>Abs : 返回结果
Abs-->>Mig : 完成迁移
Mig-->>CLI : 输出执行结果
```

图表来源
- [Cli.php](file://vendor/thinkcmf/cmf/src/console/command/Cli.php)
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

## 详细组件分析

### 组件一：迁移抽象类（AbstractMigration）
- 责任边界
  - 提供迁移生命周期钩子与环境信息。
  - 封装 SQL 执行与查询能力（execute/query/fetchRow/fetchAll）。
  - 提供 table() 快捷方法创建表对象，统一后续建表/改表/数据写入。
  - 提供数据库级操作（createDatabase/dropDatabase/hasTable）。
- 关键点
  - insert() 方法已标记弃用，推荐使用 Table 对象的 insert/save 流程。
  - postFlightCheck 会在迁移完成后校验是否存在未提交的表变更动作，确保幂等与一致性。

```mermaid
classDiagram
class AbstractMigration {
+setAdapter(adapter)
+getAdapter()
+execute(sql)
+query(sql)
+fetchRow(sql)
+fetchAll(sql)
+table(name, options)
+createDatabase(name, options)
+dropDatabase(name)
+hasTable(name)
+postFlightCheck(direction)
}
class Table {
+addColumn(name, type, options)
+addIndex(columns, options)
+addForeignKey(cols, refTable, refCols, options)
+create()
+update()
+insert(data)
+saveData()
+save()
}
AbstractMigration --> Table : "创建/使用"
```

图表来源
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

章节来源
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)

### 组件二：表对象（Table）
- 责任边界
  - 统一建表/改表动作，延迟到 save/create/update 时一次性执行。
  - 支持列、索引、外键、时间戳列等声明式定义。
  - 支持批量数据写入（bulkinsert），按列集合一致性判断是否走批量路径。
- 关键点
  - insert(data) 接受单条或多条记录，内部维护待写入队列。
  - saveData() 会根据数据结构一致性选择批量或逐条写入。
  - hasPendingActions() 用于迁移后检查是否有未完成的动作。

```mermaid
flowchart TD
Start(["开始"]) --> Define["定义列/索引/外键"]
Define --> Insert["insert(数据)"]
Insert --> Save["save()/create()/update()"]
Save --> Exec["执行动作计划"]
Exec --> Bulk{"批量写入条件满足？"}
Bulk --> |是| BulkInsert["bulkinsert(全量)"]
Bulk --> |否| LoopInsert["逐条 insert()"]
BulkInsert --> Done(["结束"])
LoopInsert --> Done
```

图表来源
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

章节来源
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

### 组件三：迁移文件样例解析
- 角色类型字段迁移
  - 在现有表上新增字段并更新，展示“改表”流程。
- 后台 API 列表迁移
  - 新建表并添加多个字段、索引，展示“建表+索引”流程。
- 多语言模板文件迁移
  - 新建表并使用适配器常量定义字段长度，展示“建表+长文本字段”流程。

章节来源
- [20230320162718_migration_cmf_role_type.php](file://vendor/thinkcmf/cmf/src/data/migrations/20230320162718_migration_cmf_role_type.php)
- [20230322153218_migration_cmf_admin_api.php](file://vendor/thinkcmf/cmf/src/data/migrations/20230322153218_migration_cmf_admin_api.php)
- [20231010004518_migration_cmf_theme_file_i18n.php](file://vendor/thinkcmf/cmf/src/data/migrations/20231010004518_migration_cmf_theme_file_i18n.php)

## 依赖分析
- 迁移框架
  - ThinkCMF 通过 xia/migration 引入 Phinx，提供迁移命令与抽象迁移类。
- 自动化执行
  - 项目文档明确列出迁移命令与自动执行脚本，便于在安装/更新后自动运行。

```mermaid
graph LR
CMF["ThinkCMF 应用"] --> MigPkg["xia/migration"]
MigPkg --> Phinx["Phinx 核心"]
CMF --> Cmd["迁移命令 php think migrate:*"]
Cmd --> MigPkg
```

图表来源
- [composer.lock](file://composer.lock)
- [AGENTS.MD](file://AGENTS.MD)

章节来源
- [composer.lock](file://composer.lock)
- [AGENTS.MD](file://AGENTS.MD)

## 性能考量
- 批量写入
  - 使用 Table::insert() 并在 saveData() 内部按列集合一致性判断是否走 bulkinsert，减少往返与开销。
- 索引设计
  - 在迁移阶段一次性声明索引，避免多次 ALTER TABLE 带来的锁与重索引成本。
- 事务与幂等
  - 建议在复杂迁移中将“结构变更 + 数据写入”包裹在事务内，保证一致性；若框架层面未自动开启，可在迁移中显式控制。
- 查询与回填
  - 使用 query()/fetchRow()/fetchAll() 进行数据读取与转换，尽量减少不必要的循环与重复查询。

## 故障排查指南
- 迁移后仍有未提交动作
  - 若迁移结束后仍存在未完成的动作，AbstractMigration::postFlightCheck 会抛出异常提示，需检查迁移文件是否遗漏 save()/create()/update() 调用。
- 弃用 API 导致行为异常
  - AbstractMigration::insert() 已弃用，请改用 Table 对象的 insert/save 流程。
- 数据写入失败
  - 检查数据结构一致性（列名与类型），确认 saveData() 是否正确识别批量写入条件。
- 命令行不可用
  - 通过 Cli 命令帮助查看可用迁移命令与用法，必要时使用帮助参数获取更详细的说明。

章节来源
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)
- [Cli.php](file://vendor/thinkcmf/cmf/src/console/command/Cli.php)

## 结论
ThinkCMF 的数据迁移以 Phinx 为基础，通过抽象迁移类与表对象实现了结构与数据的一体化管理。遵循“声明式建表/改表 + 批量数据写入”的模式，配合索引与事务策略，可在保证一致性的同时提升迁移效率。建议在生产环境严格执行备份与回滚策略，并在复杂场景中加入数据验证与日志记录，确保迁移过程可控、可观测。

## 附录

### A. 数据操作实践清单
- 插入
  - 使用 Table::insert([...]) 写入单条或多条记录，最后调用 save()/create()/update() 提交。
- 更新
  - 在迁移中直接通过结构变更（如 addColumn/renameColumn）或在后续业务逻辑中使用 ORM/查询构造器进行更新。
- 删除
  - 通过 removeColumn/removeIndex/addForeignKey(drop)/rename 等动作实现结构层面的删除；数据层面可通过 truncate 或条件删除。
- 查询
  - 使用 AbstractMigration::query()/fetchRow()/fetchAll() 读取数据，进行转换后再写入。

章节来源
- [AbstractMigration.php](file://vendor/xia/migration/phinx/src/Phinx/Migration/AbstractMigration.php)
- [Table.php](file://vendor/xia/migration/phinx/src/Phinx/Db/Table.php)

### B. 最佳实践
- 备份优先
  - 迁移前对关键表进行备份，保留回滚点。
- 分步迁移
  - 大规模数据迁移拆分为多步，先结构后数据，逐步验证。
- 数据验证
  - 迁移后执行抽样校验与完整性检查，确保字段、索引、数据一致。
- 日志与监控
  - 记录迁移耗时、影响行数、错误堆栈，便于定位问题。

### C. 常见问题与解法
- 迁移报错“存在未提交动作”
  - 检查是否遗漏 save()/create()/update() 调用。
- 写入性能差
  - 确认数据结构一致性，启用批量写入；避免在循环中频繁 insert/save。
- 索引缺失导致查询慢
  - 在迁移阶段一次性声明索引，避免后续补索引带来的代价。

### D. 命令与自动化
- 常用命令
  - 创建迁移、执行迁移、回滚迁移等命令详见项目文档。
- 自动执行
  - 安装/更新后自动执行迁移，确保部署一致性。

章节来源
- [AGENTS.MD](file://AGENTS.MD)