简体   繁体   中英

Adding Relations to Laravel Factory Model

I'm trying to add a relation to a factory model to do some database seeding as follows - note I'm trying to add 2 posts to each user

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

But its throwing the following error

Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::s  
ave() must be an instance of Illuminate\Database\Eloquent\Model, instance 
of Illuminate\Database\Eloquent\Collection given

I think its something to do with saving a collection. If re-write the code by calling each factory model for the post separately it seems to work. Obviously this isn't very elegant because if I want to persist 10 or post to each user then I'm having to decalare 10 or lines unless I use some kind of for loop.

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

* UPDATED *

Is there any way to nest the model factory a 3rd level deep?

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

Since Laravel 5.6 there is a callback functionsafterCreating & afterMaking allowing you to add relations directly after creation/make:

$factory->afterCreating(App\User::class, function ($user, $faker) {
    $user->saveMany(factory(App\Post::class, 10)->make());
});

$factory->afterMaking(App\Post::class, function ($post, $faker) {
    $post->save(factory(App\Comment::class)->make());
});

Now

factory(App\User::class, 50)->create()

will give you 50 users with each having 10 posts and each post has one comment.

Try this. It worked for me:

factory(\App\Models\Auth\User::class, 300)->create()->each(function ($s) {
                    $s->spots()->saveMany(factory(\App\Models\Spots\Parking::class, 2)->create()->each(function ($u) {
                            $u->pricing()->save(factory(\App\Models\Payment\Pricing::class)->make());
                    }));
                    $s->info()->save(factory(\App\Models\User\Data::class)->make());
            });

For a 3rd level nested relationship, if you want to create the data with the proper corresponding foreign keys, you can loop through all the results from creating the posts, like so:

factory(App\User::class, 50)->create()->each(function($u) {
    $u->posts()
        ->saveMany( factory(App\Post::class, 2)->make() )
        ->each(function($p){
            $p->comments()->save(factory(App\Comment::class)->make());
        });
});

To answer the initial problem / error message:

The problem indeed has to do with saving the data. Your code:

$u->posts()->save(factory(App\Post::class, 2)->make());

... should be changed to

$u->posts()->saveMany(factory(App\Post::class, 2)->make());

From the laravel docs :

You may use the createMany method to create multiple related models:

$user->posts()->createMany( factory(App\\Post::class, 3)->make()->toArray() );

That means:

  • when only creating one model with the factory, you should use save() or create()
  • when creating multiple models with the factory, you should use saveMany() or createMany()
$factory->define(User::class, function (Faker $faker) {
return [
    'name' => $faker->name,
    'email' => $faker->unique()->safeEmail,
    'email_verified_at' => now(),
    'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // 
password
    'remember_token' => Str::random(10),
];
});

$factory->define(Post::class, function ($faker) use ($factory) {
return [
    'title' => $faker->sentence(3),
    'content' => $faker->paragraph(5),
    'user_id' => User::pluck('id')[$faker->numberBetween(1,User::count()-1)]
];
});

Let me, explain How to add multi level of relationship of call factory in Laravel 9 By concept of new school and old school. The new school is:

 \App\Models\Author::factory()
        ->has(
            \App\Models\Article::factory(1)
                ->has(
                    \App\Models\Comment::factory(9)
                        ->has(
                            \App\Models\Reply::factory(2)
                        )

                ))->create();`enter code here`

That's for Laravel 9. There's anthor way call Magic method. let me explain that:

 \App\Models\Author::factory()->hasArticles(1)->hasComments(9)->hasReplies(2)->create();

this hasArticles() is the name of method of relationship in parent model should convert the name with has. for example: comments() convert to hasComments() .

Now lets explain old school that's still prefect in some cases and still works good with Laravel 9.

        \App\Models\Author::factory(1)->create()
        ->each(function($a) {
            $a->articles()->saveMany( \App\Models\Article::factory(2)->create() )
            ->each(function($p){
                $p->comments()->saveMany(\App\Models\Comment::factory(5))->create()
                    ->each(function($r){
                    $r->replies()->saveMany(\App\Models\Reply::factory(5))->create();
                });
            });
    });

of course you can replace method saveMany() by save() as your relationship you have. also you can replace method create() by make() if you want to doesn't save in database for test purposes.

Enjoy.

In version laravel 9, use like this

<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
 */
class PostFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'user_id' => User::factory(),
            'title' => fake()->paragraph()
        ];
    }
}```

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM