Laravel:使用模型工厂生成一对多关系

时间:2018-03-02 20:27:32

标签: laravel eloquent one-to-many factory

我在使用Laravel中的工厂为模型生成多个一对多关系时遇到问题。工厂似乎每个俱乐部只生成一个ClubFixture,当他们应该为每个俱乐部产生5个ClubFixtures。

模型

分会

<?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';
}

模态工厂

$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']),
    ];
});

数据库播种机

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

预期结果:每个俱乐部应该有5个ClubFixtures与之关联

实际结果:有些俱乐部没有ClubFixtures,有些只有一个。

我已经尝试了这个answer,但是因为saveMany不存在且关系为空而得到错误。

您可以下载SQL结果数据库here

有人可以告诉我这里做错了吗?

5 个答案:

答案 0 :(得分:2)

试试这个

模态工厂

$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']),
    ];
});

答案 1 :(得分:2)

您的模型&amp; 模型工厂似乎很好,但是将它们与这个播种机一起使用:

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])
            );
        });
    }
}

确保在俱乐部模型的saveMany()关系方法上调用fixtures()方法。最后,还会覆盖5个ClubFixtures的每次迭代的club_id,以防止再创建一个俱乐部。否则你最终会得到600个俱乐部(而不是100个俱乐部)&amp; 500 ClubFixtures。

答案 2 :(得分:2)

您应该这样做

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

用于ClubFixture工厂。

答案 3 :(得分:2)

作为@baikho答案的替代方法,您可以直接指定外键,而不必使用->saveMany()方法。我个人使用此功能,因为我通常不使用->hasMany()和其他关系方法。

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]);
        });
    }
}

请注意,使用此语法时,您也想将->make()方法更改为->create()方法。

如果您的数据库模式不是第三种规范化形式或具有某些条件字段,这也将很有帮助。

例如,如果用户(父表)是男性,则“服装”(子表)可以具有某些与用户是女性不同的选项。这些字段没有外键,但以用户为条件。

最后,您可以删除定义$ c变量的PHPDoc注释,只需将类名直接弹出到函数参数中即可。这样更干净,更清晰。

答案 4 :(得分:1)

这是我实现这一目标的方式:-

User.php(模型)

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

DatabaseSeeder(种子)

   $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(工厂)

$factory->define(UserPortfolio::class, function (Faker $faker) {
    return [
        'user_id'   => factory('App\User')->create()->id,
         ......