Laravel 11 迁移文件编写

数据库迁移是 Laravel 中用于管理数据库结构的版本控制系统,它允许你使用 PHP 代码定义和修改数据库表,并轻松在团队中同步。 本章将系统讲解如何编写迁移文件,包括创建表、修改表、添加字段、索引和外键约束。

🎯 什么是数据库迁移?

迁移就像数据库的版本控制,让你能够轻松地创建和修改数据库表。每一条迁移文件都记录了数据库的一次变更(如创建表、添加字段等),并且可以按需回滚。 使用迁移的好处包括:

  • 团队协作时保持数据库结构一致。
  • 不需要手动执行 SQL,减少错误。
  • 轻松回滚到之前的状态。
  • 与代码一起进行版本控制。

📦 创建迁移文件

使用 Artisan 命令 make:migration 创建迁移文件:

php artisan make:migration create_users_table
php artisan make:migration add_email_verified_at_to_users_table

命令选项:

  • --create=表名:指定要创建的表,Laravel 会自动生成表创建模板。
  • --table=表名:指定要修改的表,Laravel 会生成修改表结构模板。

例如:

php artisan make:migration create_posts_table --create=posts
php artisan make:migration add_category_id_to_posts_table --table=posts

生成的迁移文件位于 database/migrations 目录,文件名包含时间戳以避免冲突。

📄 迁移文件结构

一个典型的迁移文件包含 updown 两个方法:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};
  • up():执行迁移时运行的方法,用于创建/修改数据库结构。
  • down():回滚迁移时运行的方法,用于撤销 up() 所做的更改。
💡 提示: Laravel 11 使用匿名类作为迁移的默认形式,这样更加简洁。你也可以通过 --class 选项生成传统的命名类。

🔧 常用字段类型

在迁移中可以使用丰富的字段类型,以下是常用的几种:

  • $table->id(); — 自增主键,等价于 bigIncrements('id')
  • $table->string('name', 100); — VARCHAR 字段,可指定长度。
  • $table->text('description'); — TEXT 字段。
  • $table->integer('age'); — INT 字段。
  • $table->bigInteger('user_id'); — BIGINT 字段。
  • $table->boolean('is_active'); — BOOLEAN/TINYINT 字段。
  • $table->date('birthday'); — DATE 字段。
  • $table->datetime('published_at'); — DATETIME 字段。
  • $table->timestamp('created_at')->nullable(); — TIMESTAMP 字段。
  • $table->json('metadata'); — JSON 字段。
  • $table->enum('status', ['active', 'inactive']); — ENUM 字段(但注意 MySQL 的 ENUM 有一些限制)。
  • $table->timestamps(); — 创建 created_atupdated_at 字段。
  • $table->softDeletes(); — 添加 deleted_at 字段(软删除)。

还有许多其他类型,如 floatdoublecharbinary 等,可根据需要查阅文档。

📌 字段修饰符

字段定义后可以链式调用修饰符:

$table->string('email')->unique();               // 唯一索引
$table->string('name')->nullable();              // 允许 NULL
$table->integer('age')->default(18);             // 默认值
$table->text('content')->comment('文章内容');    // 添加注释
$table->timestamp('published_at')->useCurrent(); // 使用 CURRENT_TIMESTAMP

🔗 索引与外键

创建索引
$table->index('email');                 // 普通索引
$table->unique('email');                // 唯一索引
$table->fullText('content');            // 全文索引(MySQL)
$table->primary('id');                  // 主键(通常用 id() 自动创建)

也可以创建复合索引:

$table->index(['user_id', 'created_at']);
外键约束
$table->foreignId('user_id')
      ->constrained()
      ->onDelete('cascade');

上面的方法会为 user_id 字段创建外键,引用 users 表的 id 字段。constrained() 可以指定表名和字段名,例如 constrained('users', 'id')

如果你使用的是 unsignedBigInteger,可以手动创建外键:

$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
⚠️ 注意: 外键约束要求被引用的表必须先存在。如果迁移顺序不对,可能会报错。可以使用 --foreign 选项或确保迁移顺序正确。

✍️ 修改表结构

使用 Schema::table 可以修改已有表:

public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('avatar')->nullable()->after('email');
        $table->renameColumn('name', 'full_name');
        $table->dropColumn('age');
    });
}

常用的修改方法:

  • addColumn / 直接调用字段方法添加字段。
  • renameColumn — 重命名字段。
  • dropColumn — 删除字段。
  • change — 修改字段类型(需要安装 doctrine/dbal 扩展)。
修改字段类型(需先安装 doctrine/dbal)
composer require doctrine/dbal

然后:

$table->string('name', 50)->change();

🚀 运行与回滚迁移

执行迁移
php artisan migrate

该命令会运行所有未执行过的迁移。

回滚迁移
php artisan migrate:rollback     # 回滚最近一次迁移
php artisan migrate:rollback --step=3  # 回滚最近3个迁移
重置并重新运行
php artisan migrate:reset        # 回滚所有迁移
php artisan migrate:refresh      # 回滚所有并重新执行
php artisan migrate:refresh --seed  # 回滚、重新执行并运行种子
查看迁移状态
php artisan migrate:status

🎨 实战示例:完整的博客迁移

创建 users 表迁移:

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

创建 posts 表迁移:

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->onDelete('cascade');
        $table->string('title');
        $table->string('slug')->unique();
        $table->text('content');
        $table->boolean('is_published')->default(false);
        $table->timestamp('published_at')->nullable();
        $table->timestamps();
        $table->softDeletes();

        // 复合索引
        $table->index(['user_id', 'created_at']);
    });
}

创建 comments 表迁移:

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->id();
        $table->foreignId('post_id')->constrained()->onDelete('cascade');
        $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
        $table->text('content');
        $table->timestamps();
    });
}
💡 最佳实践: 在编写迁移时,始终在 down 方法中删除表或撤销修改,确保回滚可以完整还原。使用 foreignIdconstrained 简化外键定义,注意表之间的顺序。

⚠️ 常见问题

这通常是因为被引用的表还未创建,或者字段类型不匹配。确保被引用的表在迁移顺序中先执行,且外键字段类型与引用字段一致(如都是 bigIntegerunsignedBigInteger)。

使用 Schema::table 添加字段和外键。注意如果表中已有数据,需要确保外键字段的值在引用表中存在,否则会失败。

通常采用动词+表名的形式,如 create_users_tableadd_avatar_to_users_tablechange_status_column_in_orders_table。这样能清晰表达迁移的作用。

📝 小结

迁移是 Laravel 管理数据库结构的利器,通过编写清晰、可回滚的迁移文件,可以轻松实现团队协作和版本控制。 掌握常用字段类型、索引、外键以及修改表的方法,将使你的数据库操作更加规范。 下一章我们将学习数据填充与工厂,为数据库添加测试数据。