Laravel 表结构变更必须通过迁移实现,禁止手动改库;新增字段需独立迁移文件,避免混合操作;非空字段须显式设 default() 或 nullable();高频动态需求应改用 JSON 字段或专用方案。

动态加字段必须用迁移,不能直接改数据库
Laravel 的数据表结构变更只能通过迁移实现,绕过迁移直接在数据库里加字段会导致 php artisan migrate:status 状态错乱、部署失败、团队协作冲突。Laravel 不识别“手动添加”的字段,后续 php artisan migrate 会跳过该变更,甚至可能报 SQLSTATE[42S21]: Column already exists 错误。
实操建议:
- 每次新增字段都新建一个迁移文件:
php artisan make:migration add_status_to_users_table(注意命名含add_和表名) - 不要在一个迁移里反复修改
up()——比如先加字段 A,再补加 B,这会让回滚(down())逻辑难以维护 - 如果字段要加到已有大量数据的表,避免在
up()里写$table->string('new_col')->nullable()->after('id')同时设默认值,MySQL 5.7+ 对大表加带默认值的字段会锁表,超时失败
nullable() 和 default() 的取舍很关键
给非空字段(NOT NULL)加新列时,数据库要求提供默认值或允许为空,否则迁移直接报错:SQLSTATE[HY000]: General error: 1171 All parts of a PRIMARY KEY must be NOT NULL(常见于旧版本 MySQL)。Laravel 迁移不自动推断语义,默认强制你明确意图。
实操建议:
- 新字段允许为空:用
$table->string('remark')->nullable(),上线安全,业务代码后续再填充 - 新字段不允许为空且有合理默认值:用
$table->boolean('is_active')->default(true),注意default()是数据库级默认,不是 Eloquent 模型默认 - 别写
$table->string('code')->default('')去“凑”非空——空字符串和 NULL 语义不同,后期查WHERE code IS NULL会漏数据
修改字段类型或重命名要用 separate migration
Laravel 官方不支持在单个迁移中“动态增删改”混写。比如想加字段又顺手改另一个字段长度,php artisan migrate 可能成功,但 php artisan migrate:rollback 极大概率失败,因为 down() 无法可靠逆向“混合操作”。
实操建议:
- 改字段类型(如
string改text):单独建迁移,用Schema::table(..., function (Blueprint $table) { $table->text('content')->change(); });,并确保已安装doctrine/dbal - 字段重命名:同样走独立迁移,
$table->renameColumn('old_name', 'new_name'),不要幻想在 add 字段的迁移里“顺手 rename” - 生产环境执行前,在测试库跑一遍
php artisan migrate:rollback --step=1验证down()是否真能撤回
字段动态性需求强?别硬扛迁移,换思路
如果业务频繁要求“随时加字段”(比如 SaaS 多租户自定义属性),靠迁移逐个加字段是反模式:迁移文件爆炸、回滚不可控、DBA 拒绝上线。这不是 Laravel 的设计场景,而是 schema-less 或 EAV 模式的问题。
实操建议:
- 小范围动态:用 JSON 字段存扩展属性,
$table->json('metadata'),Eloquent 中用casts自动转数组,查的时候用whereJsonContains() - 多租户定制:为每个租户建独立表(
tenant_123_users),或用共享表 +tenant_id分区,避免字段膨胀 - 真要无限字段:引入专用服务(如 PostgreSQL 的 hstore / JSONB,或外部配置中心),别让主业务表承担“动态 schema”职责
迁移是描述“确定性结构变更”的工具,不是动态配置接口。越想让它“灵活”,越容易掉进回滚失败、环境不一致、上线卡死的坑里。


















