Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel factory relationships... respect create() or make()

According to Laravel's documentation on defining relationships within model factories:

You may also attach relationships to models using Closure attributes in your factory definitions. For example, if you would like to create a new User instance when creating a Post, you may do the following:

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

The issue I'm running into is the reference to create() within the relationship definition. It seems to me that this doesn't belong here.

It works great if I am wanting to persist my relationships to the database:

factory(App\Post::class)->create();

By running the code directly above this, a new App\Post and a new App\User will be created and both persisted to the database.

But if I just want to new up the model(s) and not persist anything (at all) to the database by running:

factory(App\Post::class)->make();

It does what I want up to a certain point. A new App\Post instance is created but not persisted, however App\Comment is created and is persisted to the database.


It seems to me, that what I really want is something like this:

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => function () {
            // here I only want to declare the relationship,
            // not decide whether I want to create() or make()
            // the relationship, something like:
            return factory(App\User::class)->createOrMake()->id;

            // or perhaps something like:
            return factory(App\User::class)->id;
        }
    ];
});

The end result is that I want the related data to respect what I'm trying to do from the top of the call, down. Make everything. Or create everything.


Am I doing something wrong here? Or is this something that doesn't currently exist?

Thanks!

like image 943
Johnny Avatar asked May 23 '17 00:05

Johnny


1 Answers

You want the lazy() method for your related models!

At least in Laravel 5.5.

I found your question here because I was having the same issue. I found the answer thanks to Laravel's beautifully written code -- lazy() was defined just above the make() XDebug took me to -- and I've tried it out and it seems to work.

This is what I'm doing:

$factory->define(App\ArtItem::class, function (Faker $faker) {
    return [
        // 'id' => $faker->randomDigit,
        'slug' => $faker->unique->word,
        'artist_id' => factory(App\Artist::class)->lazy(),
        'image_id' => factory(App\Image::class)->lazy(),
        'created_at' => $faker->dateTime,
        'updated_at' => $faker->dateTime,
        'deleted_at' => $faker->dateTime,
    ];
});

Lazy() is an interesting creature that returns a closure, so you can't do factory(App\Image::class)->lazy()->id but I'm still seeing it successfully setting the correct image_id and artist_id.

I certainly hope you found the solution long before this, but maybe this'll help someone else!

like image 190
salixrosa Avatar answered Oct 16 '22 16:10

salixrosa