Laravel - Seeding Relationships

前端 未结 6 505
感情败类
感情败类 2020-12-07 14:58

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 ne

相关标签:
6条回答
  • 2020-12-07 15:30

    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

    0 讨论(0)
  • 2020-12-07 15:37
    $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.

    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]);
    
    0 讨论(0)
  • 2020-12-07 15:48

    You can do this using closures within the ModelFactory as discussed here.

    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.

    0 讨论(0)
  • 2020-12-07 15:48

    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) :)

    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).

    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.

    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.)

    <?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();
        }
    }
    
    0 讨论(0)
  • 2020-12-07 15:53

    You can use saveMany as well. For example:

    factory(User::class, 10)->create()->each(function ($user) {
        $user->posts()->saveMany(factory(Posts::class, 5)->make());
    });
    
    0 讨论(0)
  • 2020-12-07 15:53

    this worked for me in 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();
    }
    
    0 讨论(0)
提交回复
热议问题