> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 数据库迁移

> 介绍如何使用 Laravel 的迁移功能对数据库结构进行版本管理。

## 什么是迁移

迁移就像是数据库的版本控制。
团队可以共享和管理应用的数据库结构定义。

你或许有过在拉取代码后被要求「请在本地数据库手动加一列」的经历。迁移正好解决了这类问题。

迁移文件存放在 `database/migrations` 目录，文件名包含时间戳，Laravel 据此决定执行顺序。

## 创建迁移文件

使用 `make:migration` Artisan 命令：

```shell theme={null}
php artisan make:migration create_posts_table
```

Laravel 会根据迁移名推测表名并生成合适的骨架。`create_posts_table` 就会预置创建 `posts` 表的代码。

## 迁移结构

迁移类包含 `up` 与 `down` 两个方法：

* `up`：向数据库添加表、列、索引。
* `down`：撤销 `up` 的操作，用于回滚。

```php theme={null}
<?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('body');
            $table->boolean('published')->default(false);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};
```

## 常见列定义方法

`Blueprint` 类内置多种列类型。

| 方法                                  | 说明                               |
| ----------------------------------- | -------------------------------- |
| `$table->id()`                      | 自增主键（`bigIncrements('id')` 别名）   |
| `$table->string('name')`            | VARCHAR（默认 255）                  |
| `$table->text('body')`              | TEXT                             |
| `$table->integer('count')`          | INTEGER                          |
| `$table->boolean('active')`         | 用 TINYINT 存储布尔值                  |
| `$table->timestamp('published_at')` | TIMESTAMP                        |
| `$table->timestamps()`              | 同时添加 `created_at` 与 `updated_at` |
| `$table->softDeletes()`             | 添加软删除字段 `deleted_at`             |
| `$table->foreignId('user_id')`      | 外键用 `BIGINT UNSIGNED`            |

### 列修饰符

可以链式添加修饰符：

```php theme={null}
$table->string('email')->unique();
$table->string('name')->nullable();
$table->integer('votes')->default(0);
$table->string('title')->after('id'); // 在指定列之后
```

## 执行迁移

用 `migrate` 命令运行未执行的迁移。

```shell theme={null}
php artisan migrate
```

查看执行情况：

```shell theme={null}
php artisan migrate:status
```

<Warning>
  在生产环境执行迁移会出现确认提示。若需跳过可加 `--force`，但因为有可能丢失数据请谨慎操作。
</Warning>

## 回滚

回滚最近一次迁移批次：

```shell theme={null}
php artisan migrate:rollback
```

按步数回滚：

```shell theme={null}
# 回滚最近 5 次
php artisan migrate:rollback --step=5
```

回滚全部并重新运行：

```shell theme={null}
php artisan migrate:refresh
```

<Info>
  `migrate:refresh` 会重建所有表，既有数据会丢失，适合开发中的数据库重置。
</Info>

## 实战示例：创建 posts 表

以博客文章的 `posts` 表为例。

### 1. 生成迁移

```shell theme={null}
php artisan make:migration create_posts_table
```

### 2. 编辑迁移

打开 `database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php`：

```php theme={null}
<?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->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('title');
            $table->text('body');
            $table->boolean('published')->default(false);
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};
```

### 3. 执行迁移

```shell theme={null}
php artisan migrate
```

数据库中会创建 `posts` 表。

### 4. 后期追加列

后期添加列时不要修改已有迁移，而是新建迁移：

```shell theme={null}
php artisan make:migration add_excerpt_to_posts_table
```

```php theme={null}
public function up(): void
{
    Schema::table('posts', function (Blueprint $table) {
        $table->string('excerpt')->nullable()->after('title');
    });
}

public function down(): void
{
    Schema::table('posts', function (Blueprint $table) {
        $table->dropColumn('excerpt');
    });
}
```

<Tip>
  尽量不要修改已存在的迁移文件，否则会破坏与其他成员或生产环境的一致性。所有更改都以新迁移的形式添加。
</Tip>

## 迁移事件

每次迁移操作会派发[事件](/zh/events)，都继承 `Illuminate\Database\Events\MigrationEvent`。

| 类                                                | 说明                    |
| ------------------------------------------------ | --------------------- |
| `Illuminate\Database\Events\DatabaseRefreshed`   | `migrate:refresh` 完成后 |
| `Illuminate\Database\Events\MigrationsStarted`   | 迁移批次即将开始              |
| `Illuminate\Database\Events\MigrationsEnded`     | 迁移批次结束后               |
| `Illuminate\Database\Events\MigrationStarted`    | 单个迁移即将执行              |
| `Illuminate\Database\Events\MigrationEnded`      | 单个迁移完成                |
| `Illuminate\Database\Events\NoPendingMigrations` | 迁移命令判定无未执行迁移          |
| `Illuminate\Database\Events\SchemaDumped`        | 数据库结构 dump 完成         |
| `Illuminate\Database\Events\SchemaLoaded`        | 载入已有 dump             |

例如可监听 `MigrationsEnded` 在迁移完成后清理缓存：

```php theme={null}
use Illuminate\Database\Events\MigrationsEnded;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Event;

Event::listen(MigrationsEnded::class, function () {
    Cache::flush();
});
```

## 下一步

<Card title="数据库填充" icon="seedling" href="/zh/seeding">
  了解如何为迁移创建的表填充示例数据。
</Card>

<Card title="Eloquent 入门" icon="database" href="/zh/eloquent">
  学习通过 Eloquent ORM 操作迁移创建的表。
</Card>


## Related topics

- [目录结构](/zh/directory-structure.md)
- [Laravel AI SDK](/zh/ai-sdk.md)
- [数据库测试](/zh/database-testing.md)
- [数据库填充（Seeding）](/zh/seeding.md)
- [数据库配置](/zh/database.md)
