laravel迁移文件命名需遵循create_xxx_table或drop_xxx_table格式才能被自动识别为建表/删表操作;建表必须包含主键定义(如id()),外键类型须与引用字段严格一致,字符串字段需指定长度,迁移失败后应优先rollback而非删除文件,timestamps()和softdeletes()须置于字段定义之后。

php artisan make:migration 生成迁移文件时命名要带表名
迁移文件名不是随便起的,Laravel 通过文件名推断操作意图。如果想创建 users 表,必须用 create_users_table 这类命名,否则 php artisan migrate 不会自动识别为“建表”操作——它只对 create_xxx_table 和 drop_xxx_table 做特殊处理。
常见错误:运行 php artisan make:migration add_phone_to_users,本意是加字段,但文件名不含 create 或 drop,Laravel 就不会在 up() 里预置 Schema::create() 模板,得手动写全逻辑。
- 建表迁移:用
php artisan make:migration create_posts_table - 改表迁移:用
php artisan make:migration add_is_published_to_posts(名字不强制,但语义要清晰) - 文件生成后立刻检查
up()和down()方法体是否为空——空的就得自己填
Schema::create() 里别漏掉 $table->id() 或 $table->bigIncrements('id')
Laravel 8+ 默认使用 id() 作为主键,它等价于 bigIncrements('id') 并自动添加 unsignedBigInteger + autoIncrement + primary。漏掉这句,表就没主键,后续 Eloquent 操作(如 save()、update())可能静默失败或报错 SQLSTATE[HY000]: General error: 1364 Field 'id' doesn't have a default value。
注意:如果手动指定主键字段名(比如叫 user_id),就不能再调用 $table->id(),得用 $table->bigIncrements('user_id')->primary(),且必须显式声明 ->primary()。
-
$table->id()是最简写法,适用于默认id主键 - 外键字段必须和被引用字段类型严格一致,比如关联
users.id(bigint),那外键也得是$table->foreignId('user_id'),不能用unsignedInteger - 字符串字段不设长度会报错:
$table->string('title')合法,$table->string('content', 65535)才能存长文本(MySQL InnoDB 限制)
php artisan migrate 执行失败后别直接删 migration 文件
执行 php artisan migrate 报错(比如字段名拼错、类型不支持),很多人第一反应是删掉刚写的迁移文件重来。这会导致数据库状态和 migrations 表记录脱节:文件删了,但 migrations 表里已记了一条记录,下次再跑 migrate 会跳过它,问题残留。
正确做法是先回滚:运行 php artisan migrate:rollback(撤回最后一批),或 php artisan migrate:rollback --step=2(撤回最近两批)。如果迁移已部分执行成功(比如表建好了但字段加失败),rollback 可能也失败,这时得手动进数据库删表,再清空 migrations 表里对应记录。
- 开发环境可放心用
php artisan migrate:fresh—— 它会删所有表再重跑全部迁移,比反复 rollback 干净 - 生产环境严禁
fresh或reset,只能写新迁移补救,比如加错字段就写一个迁移把它dropColumn - 每次改完迁移文件,务必确认
down()方法能真正逆向操作(比如create对应drop,addColumn对应dropColumn)
timestamps() 和 softDeletes() 必须在字段定义之后调用
$table->timestamps() 看似简单,但它会在底层添加 created_at 和 updated_at 两个 timestamp 字段,并自动设置默认值和更新行为。如果把它写在 $table->string('title') 之前,某些数据库驱动(尤其是 SQLite)会因字段顺序问题导致迁移失败。
同理,$table->softDeletes() 添加 deleted_at 字段,也依赖前置的字段定义完成。更隐蔽的问题是:如果表里已有数据,再补加 timestamps(),这两个字段会是 NULL,Eloquent 的 created_at 访问会返回 null 而非抛异常,容易引发业务逻辑误判。
- 始终把
timestamps()放在字段定义块末尾 - 需要非空时间戳时,显式写
$table->timestamps()->useCurrent()->useCurrentOnUpdate() - 软删除字段如果要索引加速查询,记得追加
$table->index('deleted_at')











