Summer Sale! All accounts are 50% off this week.

SteveAzz's avatar

How to create this model factory. ( Laravel 5.1)

I have the follow database structure

User

  • id
  • name
  • ...

Post

  • id
  • title
  • body
  • user_id (fk)

Vote

  • id
  • type
  • user_id (fk)
  • post_id (fk)

Now I have set up the relationships between the model like the following which should be correct.

User

/**
     * Retrieves all of the posts associated with the user.
     *
     * @return \Illumainte\Database\Relations\HasMany
     */
    public function posts()
    {
        return $this->hasMany('Reddit\Post');
    }

    /**
     * Retreives all of the votes assoicated with the user.
     * 
     * @return \Illuminate\Database\Relations\HasMany
     */
    public function vote()
    {
        return $this->hasMany('Reddit\Vote');
    }

Post

**
     * Retrieves the user associated with the post.
     *
     * @return \Illuminate\Database\Relation\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo('Reddit\User');
    }

    /**
     * Retrieves the votes associated with the post.
     * @return \Illuminate\Database\Relation\HasMany
     */
    public function votes()
    {
        return $this->hasMany('Reddit\Vote');
    }

Vote

/**
     * Retrieves the post associated with the vote.
     *
     * @return \Illuminate\Database\Relation\BelongsTo
     */
    public function post()
    {
        return $this->belongsTo('Reddit\Post');
    }

    /**
     * Retrieves the user associated with the vote.
     *
     * @return \Illuminate\Database\Relation\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo('Reddit\User');
    }

Now I manged to create the Model factory for the Post by following the documentation, but I am having trouble on how to do for the Vote where it requires two foreign keys. If you need any more information please ask.

0 likes
7 replies
phildawson's avatar

Would it be something like this?

$factory->define(App\Vote::class, function ($faker) {
    return [
        'type' => $faker->word,
        'user_id' => App\User::all()->random()->id,
        'post_id' => App\Post::all()->random()->id,
    ];
});
13 likes
SteveAzz's avatar

@phildawson Okay this worked, but it seems a bit hacky, isn't there a way to do it with 'Eloqunet' like the one from the documentation where it shows the model factory for the Post

RachidLaasri's avatar
Level 41
$factory->define(App\Vote::class, function ($faker) {
    return [
        'type' => $faker->word,
        'user_id' => factory(App\User::class)->create()->id,
        'post_id' => factory(App\Post::class)->create()->id,
    ];
});
13 likes
spidey's avatar

I agree with this solution but i have a similar case where i just want to associate existing models becouse In this solution we are creating new Users and new Posts for Votes. So i want to create few users, each of them to have some posts, and each post to have a random number of votes(comments in my case), but i`m stuck after creating posts. i have this:

    factory(App\User::class, 20)->create()->each(function($u) {
            $u->posts()->saveMany(factory(App\Post::class, 5)->make();
        });

To associate them i tried something but it`s not working: factory(App\Vote::class, 100)->create()->each(function($u) { $u->user()->sync( App\User::all()->random(3) ); });

drouten's avatar

Rachid's answer is correct but it comes with a caveat. If you do something like this 'user_id' => factory(App\User::class)->create()->id, inside your Model Factory, then every time you create a new factory object, that code will get run and a new App\User will be created, even if you pass null or a different id to the factory's create method.

This means that if you do something like factory(App\Article::class)->create(['user_id' => $user->id]) from a previously created User, you will successfully see the factory associate $user with the Article you created, BUT the Model Factory will also create another App\User in the background and throw it away.

$factory->define(App\User::class, function ($faker) {
    return [];
});

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

$user = factory(App\User::class)->create();
$article = factory(App\Article::class)->create(['user_id' => $user->id]);

The above code will create a User, then create an Article which will also create a second User and throw it away, then associate itself with the first User. This will unintuitively create two Users when it appears that only one is created.

You can get away from this by using a closure instead (if anyone can find any clear documentation on this, please share and I'd be happy to update this answer).

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

The first way of creating relationships has lead to a lot of additional models getting created--but never used--in many of our tests. which has slowed our suite down quite a bit. The second way, by passing a closure, ensures that that code will only run and create a model if nothing was passed to user_id.

4 likes

Please or to participate in this conversation.