Laravel - 播种关系
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35449226/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Laravel - Seeding Relationships
提问by djt
In Laravel, database seeding is generally accomplished through Model factories. So you define a blueprint for your Model using Faker data, and say how many instances you need:
在 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();
However, lets say your User model has a hasMany
relationship with many other Models, like a Post
model for example:
但是,假设您的 User 模型hasMany
与许多其他模型有关系,例如一个Post
模型:
Post:
id
name
body
user_id
So in this situation, you want to seed your Posts table with actualusers that were seeded in your Users table. This doesn't seem to be explicitly discussed, but I did find the following in the Laravel docs:
因此,在这种情况下,您希望使用在用户表中植入的实际用户来植入Posts表。这似乎没有明确讨论,但我确实在 Laravel 文档中找到了以下内容:
$users = factory(App\User::class, 3)
->create()
->each(function($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
So in your User factory, you create X number of Posts for each User you create. However, in a large application where maybe 50 - 75 Models share relationships with the User Model, your User Seeder would essentially end up seeding the entire database with all it's relationships.
因此,在您的用户工厂中,您为您创建的每个用户创建 X 个帖子。但是,在可能有 50 - 75 个模型与用户模型共享关系的大型应用程序中,您的用户播种器实际上最终会为其所有关系播种整个数据库。
My question is: Is this the best way to handle this? The only other thing I can think of is to Seed the Users first (without seeding any relations), and then pull random Users from the DB as needed while you are seeding other Models. However, in cases where they need to be unique, you'd have to keep track of which Users had been used. Also, it seems this would add a lot of extra query-bulk to the seeding process.
我的问题是:这是处理这个问题的最佳方法吗?我能想到的唯一另一件事是首先为用户播种(不播种任何关系),然后在播种其他模型时根据需要从数据库中随机抽取用户。但是,在需要唯一性的情况下,您必须跟踪使用了哪些用户。此外,这似乎会为播种过程添加大量额外的查询批量。
回答by user6392101
You can use saveMany as well. For example:
您也可以使用 saveMany。例如:
factory(User::class, 10)->create()->each(function ($user) {
$user->posts()->saveMany(factory(Posts::class, 5)->make());
});
回答by TimmyG
You can do this using closures within the ModelFactory as discussed here.
您可以使用此处讨论的 ModelFactory 中的闭包来完成此操作。
This solution works cleanly and elegantly with seeders as well.
该解决方案也适用于播种机。
$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;
},
];
});
For your seeder, use something simple like this:
对于您的播种机,请使用如下简单的方法:
//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]);
});
NOTE: This method does not create unneeded entries in the database, instead the passed attributes are assigned BEFORE the creation of associated records.
注意:此方法不会在数据库中创建不需要的条目,而是在创建关联记录之前分配传递的属性。
回答by Develop123
Personally I think one Seeder class to manage these relations is nicer then separated seeder classes, because you have all the logic in one place, so in one look you can see what is going on. (Anyone that knows a better approach: please share) :)
我个人认为一个 Seeder 类来管理这些关系比单独的 Seeder 类更好,因为您将所有逻辑集中在一个地方,所以一眼就能看出发生了什么。(任何知道更好方法的人:请分享):)
A solution might be: one DatabaseSeeder and private methods within the class to keep the 'run' method a bit cleaner. I have this example below, which has a User, Link, LinkUser (many-to-many) and a Note (many-to-one).
一种解决方案可能是:类中的一个 DatabaseSeeder 和私有方法,以使“运行”方法更简洁一些。我在下面有这个例子,它有一个用户、链接、链接用户(多对多)和一个注释(多对一)。
For the many-to-many relations I first create all the Links, and get the inserted ids. (since the ids are auto-inc I think the ids could be fetched easier (get max), but doesn't matter in this example). Then create the users, and attach some random links to each user (many-to-many). It also creates random notes for each user (many-to-one example). It uses the 'factory' methods.
对于多对多关系,我首先创建所有链接,并获取插入的 id。(由于 id 是 auto-inc,我认为可以更轻松地获取 id(获取最大值),但在本例中无关紧要)。然后创建用户,并为每个用户附加一些随机链接(多对多)。它还为每个用户创建随机笔记(多对一示例)。它使用“工厂”方法。
If you replace the 'Link' for your 'Post' this should work. (You can remove the 'Note' section then...)
如果您为“帖子”替换“链接”,这应该可以工作。(您可以删除“注意”部分然后...)
(There is also a method to make sure you have 1 valid user with your own login credentials.)
(还有一种方法可以确保您拥有 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('[email protected]', '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();
}
}
回答by Helder Lucas
$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,
];
});
So now if you do this factory(App\Post::class, 4)->create()
it will create 4 different posts and in the process also create 4 different users.
所以现在如果你这样做,factory(App\Post::class, 4)->create()
它会创建 4 个不同的帖子,在这个过程中还会创建 4 个不同的用户。
If you want the same user for all the posts what I usually do is:
如果您希望所有帖子都使用相同的用户,我通常会做的是:
$user = factory(App\User::class)->create();
$posts = factory(App\Posts::class, 40)->create(['user_id' => $user->id]);
回答by judasane
I want to share the approach i've taken for insert many posts to many users:`
我想分享我为向许多用户插入许多帖子所采取的方法:`
factory(App\User::class, 50)->create()
->each(
function ($u) {
factory(App\Post::class, 10)->create()
->each(
function($p) use (&$u) {
$u->posts()->save($p)->make();
}
);
}
);
`
`
This workaround worked for me after being all day long looking for a way to seed the relationship
在整天寻找建立关系的方法后,这种解决方法对我有用