用模型工厂定义Laravel外键,实现一对一和一对多关系而不需要创建不必要的模型

24

最近我一直在尝试使用 Laravel 的模型工厂和 Faker,在种子数据库中进行数据填充。

对于简单的架构,使其工作起来就像呼吸一样自然。但是,在处理涉及外键和表关系的复杂DB架构时,我遇到了几个问题:

  • 一对一
  • 一对多
  • 多对多

...就像链接中描述的那样: Laravel 5.1 模型工厂中的外键.

在这个主题中,官方文档建议按照以下方式运行数据库种子:

public function run()
{
    factory(App\User::class, 50)->create()->each(function ($u) {
        $u->posts()->save(factory(App\Post::class)->make());
    });
}

...但是这种解决方案有一个问题:当使用许多数据库表并运行许多种子时(它们之间有许多关系),使用这种方法通常会创建许多不必要的模型。例如,如果我们在上面的示例之前运行了PostsTableSeeder.php,则所有这些帖子都没有与用户相关联,并且永远不会在测试和开发中使用...

因此,在寻找处理这种情况的方法时,我想出了一种对我有效并避免创建那些“孤立”模型的功能性解决方案...

我希望与大家分享这个方法,所以它只是在回答中解释了。

3个回答

44

所以这是我的解决方案:

这个例子涉及到:

  • 用户和资料(用于说明一对一关系)
  • 用户和帖子(用于说明一对多关系)

// ONE TO ONE relationship (with Users already created)
$factory->define(App\Profile::class, function (Faker\Generator $faker) {
    return [
        'user_id' => $faker->unique()->numberBetween(1, App\User::count()),
        // Rest of attributes...
    ];
});

// ONE TO MANY relationship (with Users already created)
$factory->define(App\Posts::class, function (Faker\Generator $faker) {
    $users = App\User::pluck('id')->toArray();
    return [
        'user_id' => $faker->randomElement($users),
        // Rest of attributes...
    ];
});

1
你能否建议一下如何在多对多关系中实现相同的效果?我有用户和案例,使用User_Cases表来解决多对多关系。我正在尝试编写一个工厂来填充我的数据库。 - Kenilik
1
我在种子文件中使用了多对多的方法,而不是工厂模式... 在你的种子文件中,你只需要获取两个模型(UsersCases),并循环遍历其中一个模型,在每个案例中分配另一个模型即可。 - andcl

11

以下解决方案比随机分配用户要好得多,特别是如果您需要向此模型发送额外的信息。

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    $user = factory('App\Models\User')->create(['email' => 'email@test.com',]);
    // do your relationships here (...)
    return [
        'user_id' => $user->id,
        'title'   => $faker->sentence,
        'body'    => $faker->paragraph,
        ];
    }

我看到另一个关于匿名函数使用的例子

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    return [
        'user_id' => function () {
            return factory(App\User::class)->create()->id;
        },
        'title' => $faker->sentence,
        'body'  => $faker->paragraph,
    ];
}

来源:https://laracasts.com/series/laravel-from-scratch-2017/episodes/22


使用匿名函数是在这里执行此操作的正确方法。如果您不像第二个示例中所示将用户的创建放在匿名函数中,则每次调用工厂创建/制作方法时都会创建一个新的User模型。如果您将现有用户的user_id传递给工厂方法,即使该帖子不会附加到该用户上(它将附加到您提供的user_id上),它仍将创建一个新用户。通过像第二个示例中那样使用匿名函数,只有在没有为其提供user_id时才会创建新用户。 - scorgn

4

这是我在工厂使用的外键(FK)方法。

return [
    'user_id' => $this->faker->randomElement(User::pluck('id')),
    ...
];

注意:确保您的工厂按正确顺序运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接