Laravel 11 数据填充与种子

在开发过程中,我们经常需要为数据库添加测试数据。Laravel 提供了数据填充(Seeding)功能,允许你使用 PHP 类向数据库中插入测试数据。 结合模型工厂,可以快速生成大量符合规则的数据,极大地提高开发效率。

🎯 什么是数据填充?

数据填充是将测试数据插入数据库的过程。Laravel 的 Seeder 类(位于 database/seeders 目录)允许你以编程方式定义要插入的数据。 你可以创建多个 Seeder 文件,分别处理不同表的数据,并通过一个主 Seeder 统一调用。

📦 创建 Seeder

使用 Artisan 命令创建 Seeder 类:

php artisan make:seeder UserSeeder
php artisan make:seeder PostSeeder

生成的 Seeder 位于 database/seeders 目录,初始内容如下:

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        // 在这里编写插入数据的代码
    }
}

✍️ 编写 Seeder

run 方法中,你可以使用查询构建器或 Eloquent 模型插入数据。

使用 Eloquent 插入单条数据
use App\Models\User;

public function run(): void
{
    User::create([
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'password' => bcrypt('password'),
    ]);
}
批量插入多条数据
public function run(): void
{
    for ($i = 1; $i <= 10; $i++) {
        User::create([
            'name' => "User {$i}",
            'email' => "user{$i}@example.com",
            'password' => bcrypt('password'),
        ]);
    }
}
💡 提示: 使用 User::factory()->count(50)->create() 配合模型工厂可以更轻松地生成大量数据,稍后会详细讲解。

🚀 运行 Seeder

执行所有 Seeder
php artisan db:seed

这会运行 DatabaseSeeder 类(默认位于 database/seeders/DatabaseSeeder.php),你需要在该类中调用其他 Seeder。

执行单个 Seeder
php artisan db:seed --class=UserSeeder
强制在生产环境运行(谨慎)
php artisan db:seed --force
⚠️ 注意: 在生产环境中运行 Seeder 会清空或修改数据,务必小心。通常只在开发或测试环境使用。

🏭 模型工厂(Model Factory)

模型工厂允许你定义模型的默认数据模板,然后快速生成大量随机数据。Laravel 11 默认集成了 lazycount 方法,并支持 Faker 生成各类假数据。

创建工厂
php artisan make:factory UserFactory --model=User

生成的工厂位于 database/factories 目录,内容类似:

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    protected static ?string $password;

    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }
}

fake() 是 Faker 实例的全局辅助函数,可生成姓名、邮箱、地址等各类假数据。

在 Seeder 中使用工厂
use App\Models\User;

public function run(): void
{
    // 生成 10 个用户
    User::factory()->count(10)->create();

    // 生成一个自定义数据的用户
    User::factory()->create([
        'name' => 'Admin',
        'email' => 'admin@example.com',
    ]);
}
工厂状态(States)

可以为工厂定义不同状态,以便生成特定场景的数据:

// 在 UserFactory 中添加方法
public function suspended(): static
{
    return $this->state(fn (array $attributes) => [
        'suspended_at' => now(),
    ]);
}

// 使用
User::factory()->suspended()->create();

📂 组织 Seeder

通常我们会在 DatabaseSeeder 中调用所有 Seeder,确保一次运行即可填充所有数据。

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call([
            UserSeeder::class,
            PostSeeder::class,
            CommentSeeder::class,
        ]);
    }
}

你也可以在 Seeder 内部调用其他 Seeder,实现分模块填充。

🎨 实战示例:博客系统数据填充

假设我们有用户、文章、评论三张表,我们创建一个完整的填充流程。

1. 创建 UserFactory 和 PostFactory

UserFactory 已经默认生成,PostFactory 需要手动创建:

php artisan make:factory PostFactory --model=Post
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title' => fake()->sentence(),
            'content' => fake()->paragraphs(5, true),
            'user_id' => User::factory(),  // 自动关联用户
            'is_published' => fake()->boolean(80),
            'published_at' => fake()->dateTimeBetween('-1 month', 'now'),
        ];
    }
}

2. 编写 UserSeeder

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        // 创建管理员
        User::factory()->create([
            'name' => 'Admin',
            'email' => 'admin@example.com',
        ]);

        // 创建 20 个普通用户
        User::factory()->count(20)->create();
    }
}

3. 编写 PostSeeder

namespace Database\Seeders;

use App\Models\Post;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
    public function run(): void
    {
        // 为每个用户创建 3-10 篇文章
        Post::factory()->count(100)->create();
    }
}

4. 编写 CommentSeeder

namespace Database\Seeders;

use App\Models\Comment;
use Illuminate\Database\Seeder;

class CommentSeeder extends Seeder
{
    public function run(): void
    {
        // 每篇文章生成 0-20 条评论
        Comment::factory()->count(500)->create();
    }
}

CommentFactory 中需要定义关联关系:'post_id' => Post::factory()

5. 在 DatabaseSeeder 中调用

public function run(): void
{
    $this->call([
        UserSeeder::class,
        PostSeeder::class,
        CommentSeeder::class,
    ]);
}

运行 php artisan db:seed,即可填充所有数据。

💡 高级技巧: 使用 truncate() 方法先清空表,避免重复数据。在 DatabaseSeeder 中可以调用 User::truncate(),但注意外键约束,可使用 Schema::disableForeignKeyConstraints() 临时关闭。

🔁 重置数据库并重新填充

结合迁移和填充,可以使用一条命令重建数据库并填充数据:

php artisan migrate:refresh --seed

这相当于回滚所有迁移、重新执行迁移、再运行数据填充。

🧪 高级技巧

  • 自定义 Faker 语言:在 app/Providers/AppServiceProvider 中设置 faker->locale('zh_CN') 可生成中文数据。
  • 使用工厂创建关联数据:在工厂定义中使用 User::factory() 作为关联字段,会为新用户创建文章,实现嵌套关联。
  • 在 Seeder 中调用工厂的 createMany 方法User::factory()->count(5)->has(Post::factory()->count(3))->create() 可创建用户并同时创建关联文章。
  • 使用 createUnverified() 状态:工厂默认支持 unverified 状态(如果已定义),方便生成未验证邮箱的用户。

⚠️ 常见问题

可能是命名空间不正确。检查 database/seeders/DatabaseSeeder.php 文件开头是否有 namespace Database\Seeders;。如果文件不存在,可以手动创建或使用 composer dump-autoload 刷新自动加载。

可以在 run 方法中使用 DB::transaction() 包裹数据插入代码,确保数据一致性。

使用 unique() 方法确保字段唯一,如 fake()->unique()->email()。工厂定义中已默认使用 unique()->safeEmail(),通常不会冲突。

📝 小结

数据填充是 Laravel 开发中不可或缺的工具,通过 Seeder 和工厂,你可以快速生成大量高质量的测试数据,大大提升开发效率。 掌握创建 Seeder、编写插入逻辑、使用模型工厂以及组织填充顺序,将为你的项目带来极大的便利。 下一章我们将学习如何使用 Laravel 的认证系统,快速实现用户注册和登录功能。