8

I have a problem with generating multiple one-to-many relationship for a model using factories in Laravel. The factories seem to only be generating one ClubFixture per Club, when they should be generating 5 ClubFixtures per Club.

Models

Club

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Club extends Model
{
  //Table associated with the model
  protected $table = 'clubs';

   protected $fillable = ['name', 'league', 'age_group', 'colour', 'county', 'country'];

   public function fixtures(){
     return $this->hasMany('App\ClubFixture', 'club_id', 'id');
   }
}

ClubFixture

namespace App;

use Illuminate\Database\Eloquent\Model;

class ClubFixture extends Model
{
  //Table associated with the model
  protected $table = 'club_fixtures';
}

Modal Factories

$factory->define(App\Club::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'league' => $faker->word,
        'age_group' => $faker->word,
        'colour' => $faker->hexcolor,
        'county' => $faker->state,
        'country' => $faker->country,
        'emblem' => $faker->imageUrl(80, 80),
        'banner' => $faker->imageUrl,
        'matches' => $faker->randomDigit,
        'wins' => $faker->randomDigit,
        'losses' => $faker->randomDigit,
    ];
});

$factory->define(App\ClubFixture::class, function (Faker\Generator $faker) {
    return [
        'club_id' => function () {
            return factory(App\Club::class)->create()->id;
        },
        'opposition' => $faker->name,
        'address' => $faker->address,
        'datetime' => $faker->dateTimeBetween('now', '+30 weeks'),
        'type' => $faker->randomElement(['home', 'away', 'none']),
    ];
});

Database seeder

factory(App\Club::class, 100)->create()->each(function ($u) {
     factory(App\ClubFixture::class, 5)->create();
});

Expected Result: Each Club should have 5 ClubFixtures associated with it

Actual Result: Some Clubs have no ClubFixtures, some only have one.

I've tried this answer, but get an error with saveMany does not exist and relationship is null.

You can download the SQL resulting database here.

Can someone tell me what I am doing wrong here?

5 Answers5

8

Your Models & Model Factories seem fine, but use them with this seeder instead:

use Illuminate\Database\Seeder;

class ClubSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(App\Club::class, 100)->create()->each(function($c) {
            /** @var \App\Club $c */
            $c->fixtures()->saveMany(
                factory(App\ClubFixture::class, 5)->make(['club_id' => NULL])
            );
        });
    }
}

Make sure you call the saveMany() method on the fixtures() relationship method of your Club model. Finally also override the club_id for each iteration of the 5 ClubFixtures to prevent another creation of a Club. Otherwise you'd end up with 600 Clubs (instead of 100 Clubs) & 500 ClubFixtures.

baikho
  • 5,203
  • 4
  • 40
  • 47
4

You should do this

'club_id' => function () {
            return factory(App\Club::class,5)->create()->id;
        },

for the ClubFixture factory.

Nazmus Shakib
  • 802
  • 1
  • 9
  • 18
4

As an alternative to @baikho's answer, you can specify the foreign key directly instead of using the ->saveMany() method. I use this personally since I don't usually use ->hasMany() and other relationship methods.

use Illuminate\Database\Seeder;

class ClubSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(App\Club::class, 100)->create()->each(function(App\Club $c) {
            factory(App\ClubFixture::class, 5)->create(['club_id' => $c->id]);
        });
    }
}

Note that you want to change the ->make() method to the ->create() method as well when using this syntax.

This is also helpful if your database schema is not in the third normalized form or if you have some conditional fields.

For example if a User (parent table) is a male, the Clothing (child table) can have certain options that are different than if the user was a female. Those fields don't have a foreign key, but they are conditional on the User.

Finally you can remove the PHPDoc comment defining the $c variable and just pop the classname directly into the function argument. Its a bit cleaner and more legible that way.

Symphony0084
  • 1,257
  • 16
  • 33
2

Try this

Modal Factories

$factory->define(App\Club::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'league' => $faker->word,
        'age_group' => $faker->word,
        'colour' => $faker->hexcolor,
        'county' => $faker->state,
        'country' => $faker->country,
        'emblem' => $faker->imageUrl(80, 80),
        'banner' => $faker->imageUrl,
        'matches' => $faker->randomDigit,
        'wins' => $faker->randomDigit,
        'losses' => $faker->randomDigit,
    ];
});

$factory->define(App\ClubFixture::class, function (Faker\Generator $faker) {
    return [
        'club_id' => function () {
            return factory(App\Club::class,5)->create();
        },
        'opposition' => $faker->name,
        'address' => $faker->address,
        'datetime' => $faker->dateTimeBetween('now', '+30 weeks'),
        'type' => $faker->randomElement(['home', 'away', 'none']),
    ];
});
NIKHIL NEDIYODATH
  • 2,703
  • 5
  • 24
  • 30
1

Here is how i achieved this :-

User.php (Model)

 public function UserPortfolio(){
    return $this->belongsTo(UserPortfolio::class,'id','user_id');
 }
 public function hasOnePortfolio(){
    return $this->hasOne(UserPortfolio::class,'id','user_id');
 }

DatabaseSeeder (Seeds)

   $usersCount = (int)$this->command->ask('How many users do you need ? Default : ', 10);
   factory(App\Promoter::class, $usersCount)->make()->each(function ($up) {
            $up->hasOnePortfolio()->save(factory(App\UserPortfolio::class)->make());
   });
   $this->command->info('Users Created!');
   $this->command->info('Users portfolio Updated!');

UserPortfolioFactory.php (factories)

$factory->define(UserPortfolio::class, function (Faker $faker) {
    return [
        'user_id'   => factory('App\User')->create()->id,
         ......
Vipertecpro
  • 3,056
  • 2
  • 24
  • 43