codeigniter 3 迁移失败主因是生命周期理解偏差而非语法错误:文件名须为yyyymmddhhiiss_describe_what.php,类名须为migration_describewhat;up()/down()须成对可逆;add_field()只接受索引数组字符串;已执行迁移不可修改,应新增版本补救。

CodeIgniter 3 的迁移脚本出错,90% 是因为没按框架的执行时序和约束来写,不是语法问题,而是生命周期理解偏差。
迁移类名和文件名必须严格匹配版本号+下划线+描述
CI3 迁移靠文件名解析版本顺序,不靠类名或时间戳。文件名格式是 YYYYMMDDHHIISS_describe_what.php(例如 20230816125610_create_languages_table.php),其中前14位是数字时间戳,CI3 按此升序执行。类名必须是 Migration_Create_languages_table —— 前缀 Migration_ 不可少,后续部分首字母大写、用驼峰、不能有下划线或数字开头。
- 错例:
20230816_create_langs.php→ 时间戳缺位,CI3 会跳过 - 错例:
class Create_languages_table extends CI_Migration→ 缺Migration_前缀,CI3 找不到类 - 错例:
class migration_create_languages_table→ 首字母小写,PHP 自动加载失败
up() 和 down() 必须成对且语义可逆
CI3 迁移不是“只跑一次”的脚本,php index.php migrate:latest 和 php index.php migrate:rollback 都依赖这两个方法。如果 down() 留空、抛异常、或删表却不删索引,下次回滚就卡死。
-
up()创建表时,所有UNIQUE INDEX和INDEX必须显式命名(如`languages_abbr_unique`),否则down()无法精准删除 -
down()里不要写$this->dbforge->drop_table('languages', TRUE)——TRUE参数在旧版 CI3 中可能触发未定义行为;统一用FALSE并确保表存在再删 - 避免在
up()里调用$this->db->insert()插种子数据;那是 Seeder 的事,混在一起会导致migrate:refresh失败
字段定义必须用字符串数组传给 add_field(),不能用关联数组
CI3 的 dbforge->add_field() 只接受索引数组,每个元素是一条 SQL 字段定义字符串。用关联数组(比如 ['id' => ['type' => 'INT']])看似文档写法,但在 CI3.1.13 及更早稳定版中会静默失败,表建出来只有主键。
- 对:
["`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键'", "`guid` VARCHAR(32) NOT NULL"] - 错:
['id' => ['type' => 'BIGINT', 'constraint' => 20, 'unsigned' => TRUE]]→ 兼容性差,部分驱动(如 sqlsrv)直接报错 - 注意反引号:字段名加
`是为兼容关键字(如`order`),但表名不用加,create_table('languages')即可
迁移启用后,别动已执行过的版本文件
CI3 把当前迁移版本记在数据库 migration 表里,只存一个整数(如 20230816125610)。如果你改了已执行过的迁移文件内容,再跑 migrate 不会重新执行 —— 它只看版本号是否更大。强行覆盖会导致结构与代码不一致,后期查 bug 成灾难。
- 已上线的迁移文件,哪怕发现少了个索引,也应新建一个更高版本号的迁移来补(如
20230817092230_add_guid_index_to_languages.php) - 本地开发时误改了未提交的迁移,先
php index.php migrate:rollback回退,再删掉migration表里对应行,最后重跑 - 永远在
application/config/migration.php里确认$config['migration_enabled'] = TRUE,CI3 默认是FALSE
最易被忽略的一点:迁移脚本里不要依赖任何 helper、library 或自定义模型 —— 它运行在 CLI 环境,$this->load->model() 会报错,连 base_url() 都不可用。所有逻辑必须原生、无外部依赖、纯 SQL 友好。











