Laravel - 种子关系

djt*_*djt 19 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();
Run Code Online (Sandbox Code Playgroud)

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

Post:
   id
   name
   body
   user_id
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

use*_*101 30

您也可以使用saveMany.例如:

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

  • 在看到这个答案之前我不知道“saveMany()”。谢谢。:) (2认同)

Tim*_*myG 13

您可以为讨论的ModelFactory内做到这一点使用闭包在这里.

该解决方案也适用于播种机,干净利落地工作.

$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;
        },
    ];
});
Run Code Online (Sandbox Code Playgroud)

对于播种机,请使用以下简单的方法:

//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]);
});
Run Code Online (Sandbox Code Playgroud)

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

  • 最好的答案之一。另外,现在你可以在后工厂`'user_id'=>factory(User::class)`中使用稍微简单的方法,不再需要clojure。 (2认同)

Dev*_*123 7

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

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

对于多对多关系,我首先创建所有链接,然后获取插入的ID.(因为ids是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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Hel*_*cas 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,
    ];
});
Run Code Online (Sandbox Code Playgroud)

所以现在如果你这样做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]);
Run Code Online (Sandbox Code Playgroud)