codeigniter 4 迁移需手动启用并验证:设 app/config/migrations.php 中 $enabled = true,确保 app/database/migrations/ 目录存在且权限正确,文件命名符合 yyyymmddhhiiss_*.php 格式、命名空间为 app\database\migrations;不支持单文件执行,仅按时间戳顺序运行。

CodeIgniter 4 的迁移功能本身是可用的,但默认不启用,且不能像 Laravel 那样直接跑单个迁移文件——必须通过 spark migrate 按时间戳顺序执行,跳过校验或指定文件需自定义命令。
如何开启并验证迁移系统是否就绪
迁移不是开箱即用的功能,得手动打开开关并确认环境支持。缺一环,spark migrate 就会静默失败或报“no migrations found”。
- 编辑
app/Config/Migrations.php,确保$enabled = true(CI4 默认为false) - 检查
app/Database/Migrations/目录是否存在,且权限可读——Spark 命令不会自动创建该目录 - 运行
php spark migrate:status:若提示 “No migrations found”,说明路径、命名(必须是YYYYMMDDHHIISS_*.php格式)或命名空间(namespace App\Database\Migrations;)有误 - 注意:CI4 迁移依赖
App\Database\Migrations命名空间,写成Database\Migrations或漏掉App\会导致类加载失败,错误信息是Class "Database\Migrations\XXXX" not found
为什么不能直接运行某个迁移文件
CI4 原生迁移机制只支持按版本号(时间戳)顺序执行,没有 --path 或 --file 参数。强行指定单个文件会绕过依赖检查和状态追踪,导致 migration_version 表记录错乱。
- 官方不提供
php spark migrate --file xxx.php这类能力 - 若真需要调试单个迁移(比如补跑某张表),必须用自定义命令——如
php spark make:command MigrateFileCommand,再在run()中调用$runner->force($filePath, $namespace, $dbgroup) -
force()会跳过已执行记录检查,但不会更新migration_version表,后续再跑spark migrate可能重复执行或跳过,生产环境严禁使用 - 常见误操作:把迁移文件复制到
app/Database/Migrations/后直接改时间戳来“插队”,结果因时间戳冲突导致spark migrate报Duplicate migration version
迁移文件里能写什么、不能写什么
迁移只管结构(DDL),不管数据(DML)。混用会导致不可回滚、压缩失败、团队协作断裂。
- ✅ 允许:
Schema::create()、Schema::table()->addColumn()、Schema::table()->dropColumn()、Schema::table()->addIndex() - ❌ 禁止:
DB::table('users')->where('created_at', 'delete()—— 这属于数据清洗,应拆进 Seeder 或 CLI 脚本 - ⚠️ 危险:
Schema::dropIfExists()在已有生产数据的库上执行,down()方法无法真正“恢复”被删的数据,仅能重建空表 - 注意:CI4 迁移不支持事务包装(MySQL MyISAM 引擎下更明显),大表
ALTER TABLE操作可能锁表,建议在低峰期执行并提前测试
多人协作时如何避免迁移冲突
时间戳靠本地机器生成,两人同时 spark make:migration 极易撞时间戳,造成 CI 流水线失败或本地执行顺序错乱。
- 不要手动生成时间戳;统一用
php spark make:migration create_posts_table,Spark 会基于当前毫秒生成唯一前缀(如20260512145230_create_posts_table.php) - 每次提交前,先
git pull再spark migrate,确认本地迁移版本与远程一致;若发现新迁移已存在,先执行再提交自己的 - 禁止修改已提交迁移文件的文件名或时间戳——这会让其他协作者的
migration_version表状态失效 - 上线前用
spark migrate:status对比预发/生产环境,确保所有迁移都已应用,而不是只看“没报错”就认为成功
迁移不是 SQL 脚本托管工具,它的价值在于可预测的执行顺序和明确的 up/down 边界。一旦开始往迁移里塞数据逻辑,就等于亲手拆掉版本控制的地基——后面的人要么重写,要么硬着头皮维护一坨无法 squash、不敢回滚的“伪迁移”。











