# 数据库迁移

迁移是以结构化和组织良好的方式修改数据库的便捷途径。虽然可以手动编辑 SQL 片段，但这样就必须告知其他开发者运行这些片段，且在下次部署时，还需跟踪哪些更改需要应用到生产服务器。

migrations 数据库表用于记录已运行的迁移，因此只需确保迁移文件到位，并运行 `spark migrate` 命令即可将数据库更新至最新状态。还可使用 `spark migrate --all` 来包含所有命名空间下的迁移。

## 迁移文件名

迁移文件名由时间戳前缀、下划线（`_`）和描述性名称（类名）组成。

- 2024-09-08-013653_AddBlogTable.php

每个迁移都以创建时的时间戳（2024-09-08-013653）编号，格式为 YYYY-MM-DD-HHIISS。

描述性名称（AddBlogTable）是 PHP 类名，因此必须命名为有效的类名。

前缀中的年、月、日和时间可以用连字符（`-`）、下划线（`_`）分隔，也可以不分隔。例如：

- 2012-10-31-100538_AlterBlogTrackViews.php

- 2012_10_31_100539_AlterBlogAddTranslations.php

- 20121031100537_AddBlog.php

迁移按数字顺序正向或反向运行，具体取决于所选的操作。这有助于防止团队协作时的编号冲突。

## 创建迁移

这是一个包含博客功能的新站点的首次迁移。所有迁移文件都存放于 app/Database/Migrations/ 目录，文件名类似于 2022-01-31-013057_AddBlog.php。

    <?php

    namespace App\Database\Migrations;

    use CodeIgniter\Database\Migration;

    class AddBlog extends Migration
    {
        public function up()
        {
            $this->forge->addField([
                'blog_id' => [
                    'type'           => 'INT',
                    'constraint'     => 5,
                    'unsigned'       => true,
                    'auto_increment' => true,
                ],
                'blog_title' => [
                    'type'       => 'VARCHAR',
                    'constraint' => '100',
                ],
                'blog_description' => [
                    'type' => 'TEXT',
                    'null' => true,
                ],
            ]);
            $this->forge->addKey('blog_id', true);
            $this->forge->createTable('blog');
        }

        public function down()
        {
            $this->forge->dropTable('blog');
        }
    }

可分别通过 `$this->db` 和 `$this->forge` 使用数据库连接和数据库 Forge 类。

此外，也可通过命令行调用来生成迁移文件骨架。详见 命令行工具 中的 make:migration。

备注

由于迁移类是 PHP 类，因此每个迁移文件中的类名必须唯一。

### 外键

当表包含外键时，删除表或列的操作常会导致迁移失败。若要在运行迁移时临时绕过外键检查，请使用数据库连接中的 `disableForeignKeyChecks()` 和 `enableForeignKeyChecks()` 方法。

    <?php

    namespace App\Database\Migrations;

    use CodeIgniter\Database\Migration;

    class AddBlog extends Migration
    {
        public function up()
        {
            $this->db->disableForeignKeyChecks();

            // Migration rules would go here..

            $this->db->enableForeignKeyChecks();
        }
    }

### 数据库组

迁移仅针对单个数据库组运行。如果在 app/Config/Database.php 中定义了多个组，默认将针对该配置文件中指定的 `$defaultGroup` 运行。

有时可能需要为不同的数据库组使用不同的架构。例如，一个数据库用于存储常规站点信息，另一个用于存储核心业务数据。

通过在迁移类中设置 `$DBGroup` 属性，可确保迁移仅针对特定组运行。此名称必须与数据库组名称完全匹配：

    <?php

    namespace App\Database\Migrations;

    use CodeIgniter\Database\Migration;

    class AddBlog extends Migration
    {
        protected $DBGroup = 'alternate_db_group';

        public function up()
        {
            // ...
        }

        public function down()
        {
            // ...
        }
    }

备注

记录已运行迁移的 migrations 表将始终在默认数据库组中创建。

### 命名空间

迁移类库可利用 `$psr4` 属性匹配目录名，自动扫描在 app/Config/Autoload.php 中定义或从外部源（如 Composer）加载的所有命名空间。它将包含在 Database/Migrations 中找到的所有迁移。

每个命名空间都有独立的版本序列，这有助于在不影响其他命名空间的情况下对各个模块（命名空间）进行升级或回滚。

例如，假设在 Autoload 配置文件中定义了以下命名空间：

    $psr4 = [
        APP_NAMESPACE => APPPATH,
        'MyCompany'   => ROOTPATH . 'MyCompany',
    ];

系统将在 APPPATH/Database/Migrations 和 ROOTPATH/MyCompany/Database/Migrations 中查找迁移。这使得在可复用的模块化代码套件中包含迁移变得非常简单。

## 命令行工具

CodeIgniter 自带了多个可从命令行使用的 命令，以协助处理迁移任务。这些工具为开发者提供了便利，其核心是调用了 MigrationRunner 类中的相关方法。

### migrate

使用所有可用的迁移来迁移数据库组：

    php spark migrate

可为 `migrate` 使用以下选项：

- `-g` - 指定数据库组。若指定，则仅运行该组的迁移；若未指定，则运行所有迁移。

- `-n` - 选择命名空间，否则默认使用 `App`。

- `--all` - 将所有命名空间迁移至最新。

本例将在 test 数据库组中运行 `Acme\Blog` 命名空间下的所有新迁移：

Unix 环境：

    php spark migrate -g test -n Acme\\Blog

Windows 环境：

    php spark migrate -g test -n Acme\Blog

使用 `--all` 选项时，系统将扫描所有命名空间以查找未运行的迁移。这些迁移会被汇总，并按创建日期排序。这有助于最大限度地减少主应用与各模块之间可能存在的冲突。

### migrate:rollback

将所有迁移回滚到初始状态（即迁移 0）：

    php spark migrate:rollback

可为 `migrate:rollback` 使用以下选项：

- `-b` - 选择批次：使用正整数指定批次。

- `-f` - 强制跳过确认提示，此提示仅在生产环境中出现。

### migrate:refresh

通过先回滚所有迁移，再重新运行所有迁移来重置数据库状态：

    php spark migrate:refresh

可为 `migrate:refresh` 使用以下选项：

- `-g` - 指定数据库组。若指定，则仅运行该组的迁移；若未指定，则运行所有迁移。

- `-n` - 选择命名空间，否则默认使用 `App`。

- `--all` - 刷新所有命名空间。

- `-f` - 强制跳过确认提示，此提示仅在生产环境中出现。

### migrate:status

列出所有迁移及其运行时间；若尚未运行则显示 '--'：

    php spark migrate:status

    ...

    +----------------------+-------------------+-----------------------+---------+---------------------+-------+
    | Namespace            | Version           | Filename              | Group   | Migrated On         | Batch |
    +----------------------+-------------------+-----------------------+---------+---------------------+-------+
    | App                  | 2022-04-06-234508 | CreateCiSessionsTable | default | 2022-04-06 18:45:14 | 2     |
    | CodeIgniter\Settings | 2021-07-04-041948 | CreateSettingsTable   | default | 2022-04-06 01:23:08 | 1     |
    | CodeIgniter\Settings | 2021-11-14-143905 | AddContextColumn      | default | 2022-04-06 01:23:08 | 1     |
    +----------------------+-------------------+-----------------------+---------+---------------------+-------+

可为 `migrate:status` 使用以下选项：

- `-g` - 指定数据库组。若指定，则仅检查该组的迁移；若未指定，则检查所有迁移。

### make:migration

在 app/Database/Migrations 中创建一个迁移文件骨架。该命令会自动添加当前时间戳。生成的类名是文件名的大驼峰（PascalCase）版本。

    php spark make:migration <class> [options]

可为 `make:migration` 使用以下选项：

- `--namespace` - 设置根命名空间。默认值：`APP_NAMESPACE`。

- `--suffix` - 在类名后追加组件标题。

以下选项用于生成数据库 Session 的迁移文件：

- `--session` - 生成数据库 Session 的迁移文件。

- `--table` - 用于数据库 Session 的表名。默认值：`ci_sessions`。

- `--dbgroup` - 用于数据库 Session 的数据库组。默认值：`default`。

## 迁移配置

下表列出了 app/Config/Migrations.php 中所有的迁移配置选项。

配置项 | 默认值 | 选项 | 描述
--- | --- | --- | ---
enabled | true | true / false | 启用或禁用迁移。
table | migrations | 无 | 存储架构版本号的表名。 此表始终在默认数据库组（`$defaultGroup`）中创建。
timestampFormat | Y-m-d-His_ |  | 创建迁移时使用的时间戳格式。
lock | false | true / false | 启用分布式锁，以防止多进程环境（如 Kubernetes）中的并发迁移。

## 类参考

class CodeIgniter\Database\MigrationRunner
  findMigrations()
    返回:

      迁移文件数组

    返回类型:

      `array`

    返回在 `path` 属性中找到的迁移文件名数组。

  latest($group)
    参数:

      - $group (`mixed`) -- 数据库组名称，若为 null 则使用默认组。

    返回:

      成功返回 `true`，失败返回 `false`

    返回类型:

      `bool`

    定位命名空间（或所有命名空间）的迁移，确定尚未运行的迁移，并按版本顺序运行（各命名空间交替进行）。

  regress($targetBatch, $group)
    参数:

      - $targetBatch (`int`) -- 要回滚到的目标批次；正整数指定批次，0 表示回滚所有，负数表示相对批次（例如 -3 表示“回滚三个批次”）。

      - $group (`?string`) -- 数据库组名称，若为 null 则使用默认组。

    返回:

      成功返回 `true`，失败或未发现迁移返回 `false`

    返回类型:

      `bool`

    按批次将更改回滚到之前的状态。

        <?php

        $migration->regress(5);
        $migration->regress(-1);

  force($path, $namespace, $group)
    参数:

      - $path (`mixed`) -- 有效迁移文件的路径。

      - $namespace (`mixed`) -- 所提供迁移的命名空间。

      - $group (`mixed`) -- 数据库组名称，若为 null 则使用默认组。

    返回:

      成功返回 `true`，失败返回 `false`

    返回类型:

      `bool`

    强制运行单个迁移文件，不论顺序或批次。系统会根据是否已迁移来自动检测是执行 `up()` 还是 `down()`。

    备注

    建议仅在测试时使用此方法，否则可能导致数据一致性问题。

  setNamespace($namespace)
    参数:

      - $namespace (`string|null`) -- 应用命名空间。`null` 表示所有命名空间。

    返回:

      当前 MigrationRunner 实例

    返回类型:

      `CodeIgniter\Database\MigrationRunner`

    设置类库查找迁移文件的命名空间：

        <?php

        $migration->setNamespace($namespace)->latest();

    备注

    如果设置为 `null`，则会在所有命名空间中查找迁移文件。

  setGroup($group)
    参数:

      - $group (`string`) -- 数据库组名称。

    返回:

      当前 MigrationRunner 实例

    返回类型:

      `CodeIgniter\Database\MigrationRunner`

    设置类库查找迁移文件的数据库组：

        <?php

        $migration->setGroup($group)->latest();