Laravel - 种子关系

时间:2016-02-17 06:15:53

标签: php laravel

在Laravel中,数据库种子通常通过模型工厂完成。因此,您可以使用Faker数据为模型定义蓝图,并说明您需要多少实例:

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

$user = factory(App\User::class, 50)->create();

但是,假设您的用户模型与许多其他模型有hasMany关系,例如Post模型:

Post:
   id
   name
   body
   user_id

因此,在这种情况下,您希望使用在Users表中播种的 实际 用户为您的帖子加密。这似乎没有明确讨论过,但我确实在Laravel文档中找到了以下内容:

$users = factory(App\User::class, 3)
    ->create()
    ->each(function($u) {
         $u->posts()->save(factory(App\Post::class)->make());
    });

因此,在您的用户工厂中,您为每个创建的用户创建X个帖子。但是,在一个大型应用程序中,可能有50-75个模型与用户模型共享关系,您的用户播种器实际上最终会将整个数据库与其所有关系一起播种。

我的问题是:这是处理此问题的最佳方法吗?我能想到的唯一另一件事是首先播种用户(没有播种任何关系),然后在播种其他模型时根据需要从数据库中随机抽取用户。但是,如果它们需要是唯一的,您必须跟踪哪些用户已被使用。此外,这似乎会为播种过程增加许多额外的查询 - 批量。

6 个答案:

答案 0 :(得分:25)

您也可以使用saveMany。例如:

factory(User::class, 10)->create()->each(function ($user) {
    $user->posts()->saveMany(factory(Posts::class, 5)->make());
});

答案 1 :(得分:8)

您可以使用ModelFactory中的闭包执行此操作,如here所述。

此解决方案也适用于播种机。

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

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

对于播种机,请使用以下简单的东西:

//create 10 users
factory(User::class, 10)->create()->each(function ($user) {
    //create 5 posts for each user
    factory(Post::class, 5)->create(['user_id'=>$user->id]);
});

注意:此方法不会在数据库中创建不需要的条目,而是在创建关联记录之前分配传递的属性。

答案 2 :(得分:6)

就我个人而言,我认为管理这些关系的一个Seeder类比分离的播种器类更好,因为你在一个地方拥有所有的逻辑,所以在一个看起来你可以看到发生了什么。 (任何了解更好方法的人:请分享):)

解决方案可能是:一个DatabaseSeeder和类中的私有方法,以保持'运行'方法有点清洁。我在下面有这个例子,它有一个User,Link,LinkUser(多对多)和一个Note(多对一)。

对于多对多关系,我首先创建所有链接,然后获取插入的ID。 (因为id是auto-inc我认为id可以更容易获取(获得最大值),但在这个例子中并不重要)。然后创建用户,并为每个用户附加一些随机链接(多对多)。它还为每个用户创建随机笔记(多对一示例)。它使用'工厂'方法

如果您更换了'链接'为你的'发布'这应该工作。 (您可以删除'注意'部分然后......)

(还有一种方法可以确保您拥有1位拥有自己登录凭据的有效用户。)

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // Create random links
        factory(App\Link::class, 100)->create();

        // Fetch the link ids
        $link_ids = App\Link::all('id')->pluck('id')->toArray();

        // Create random users
        factory(App\User::class, 50)->create()->each(function ($user) use ($link_ids) {

            // Example: Many-to-many relations
            $this->attachRandomLinksToUser($user->id, $link_ids);

            // Example: Many-to-one relations
            $this->createNotesForUserId( $user->id );
        });

        // Make sure you have a user to login with (your own email, name and password)
        $this->updateCredentialsForTestLogin('john@doe.com', 'John Doe', 'my-password');
    }

    /**
     * @param $user_id
     * @param $link_ids
     * @return void
     */
    private function attachRandomLinksToUser($user_id, $link_ids)
    {
        $amount = random_int( 0, count($link_ids) ); // The amount of links for this user
        echo "Attach " . $amount . " link(s) to user " . $user_id . "\n";

        if($amount > 0) {
            $keys = (array)array_rand($link_ids, $amount); // Random links

            foreach($keys as $key) {
                DB::table('link_user')->insert([
                    'link_id' => $link_ids[$key],
                    'user_id' => $user_id,
                ]);
            }
        }
    }

    /**
     * @param $user_id
     * @return void
     */
    private function createNotesForUserId($user_id)
    {
        $amount = random_int(10, 50);
        factory(App\Note::class, $amount)->create([
            'user_id' => $user_id
        ]);
    }

    /**
     * @param $email
     * @param $name
     * @param $password
     * @return void
     */
    private function updateCredentialsForTestLogin($email, $name, $password)
    {
        $user = App\User::where('email', $email)->first();
        if(!$user) {
            $user = App\User::find(1);
        }
        $user->name = $name;
        $user->email = $email;
        $user->password = bcrypt($password); // Or whatever you use for password encryption
        $user->save();
    }
}

答案 3 :(得分:5)

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

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

现在,如果您执行此操作factory(App\Post::class, 4)->create(),它将创建4个不同的帖子,并且在此过程中还会创建4个不同的用户。

如果您希望所有帖子都使用相同的用户,我通常会这样做:

$user = factory(App\User::class)->create();
$posts = factory(App\Posts::class, 40)->create(['user_id' => $user->id]);

答案 4 :(得分:0)

我想分享我为很多用户插入很多帖子所采用的方法:`

namespace mars
{   
    namespace  
    {  
        int test = 0;   
    }   
}   
int main()   
{   
    // Adding a watch on test does not work.   
    mars::test++;   
    return 0;   
}

`

这种解决方法在我整天寻找种子关系的方法之后为我工作

答案 5 :(得分:0)

这在laravel v8中对我有用

for ($i=0; $i<=2; $i++) {
    $user = \App\Models\User::factory(1)->create()->first();
    $product = \App\Models\Product::factory(1)->create(['user_id' => $user->id])->first();
}