Laravel:种子表有几个属于使用工厂的关系

Posted

技术标签:

【中文标题】Laravel:种子表有几个属于使用工厂的关系【英文标题】:Laravel: Seeding table with several belongs to relationship using factories 【发布时间】:2020-09-20 17:13:43 【问题描述】:

tl;博士:

我正在使用 Laravel 和 Eloquent 并尝试为数据库添加种子并将多个属于关系附加到一个表,但我收到错误:

调用未定义的方法 Illuminate\Database\Eloquent\Relations\BelongsTo::attach()

详细解释:

我在 Eloquent 中使用 php 7.1Laravel 6。我正在尝试播种测试数据库。我有以下关系结构:

一个用户有多个帖子/一个帖子属于一个用户

一个类别有多个帖子/一个帖子属于一个类别

用户模型:

class User extends Model

    protected $table = 'users';

    public function posts()
    
        return $this->hasMany('App\Models\Post');
    
 

类别型号:

class Category extends Model

    protected $table = 'categories';

    public function posts()
    
        return $this->hasMany('App\Models\Category');
    

后模型:

class Post extends Model

    protected $table = 'posts';

    public function category()
    
        return $this->belongsTo('App\Models\Category');
    

    public function user()
    
        return $this->belongsTo('App\Models\User');
    

用户工厂:

$factory->define(User::class, function (Faker $faker) 
    return [
        'id' => $faker->unique()->randomNumber(3),
        'name' => $faker->name(),
    ];
);

类别工厂:

$factory->define(Category::class, function (Faker $faker) 
    return [
        'id' => $faker->unique()->randomNumber(3),
        'name' => $faker->word(),
    ];
);

后工厂:

$factory->define(User::class, function (Faker $faker) 
    return [
        'id' => $faker->unique()->randomNumber(3),
        'name' => $faker->realText($maxNbChars = 200, $indexSize = 2),
    ];
);

如果我只有用户和帖子,我知道我可以通过以下方式为数据库播种:

public function seed()

    $users = factory(App\User::class, 10)
        ->create()
        ->each(function ($user) 
            $user->posts()->createMany(factory(App\Post::class, 10)->make()->toArray());
        );

但显然这在我的情况下不起作用。我尝试了以下方法:

public function seed()

    factory(User::class, 10)->create();
    factory(Category::class, 10)->create();
    factory(Post::class, 100)->create()->each(function ($post)
        $post->user()->attach(User::all()->random(1));
        $post->category()->attach(Category::all()->random(1));
    );

以及使用 make/save 而不是 create 的相同功能:

public function seed()
    
        factory(User::class, 10)->create();
        factory(Category::class, 10)->create();
        factory(Post::class, 100)->make()->each(function ($post) 
            $post->user()->attach(User::all()->random(1));
            $post->category()->attach(Category::all()->random(1));
        )->save();
    

但在这两种情况下,我都会收到错误:

调用未定义的方法 Illuminate\Database\Eloquent\Relations\BelongsTo::attach()

【问题讨论】:

【参考方案1】:

你必须使用associate():

public function seed()

    factory(User::class, 10)->create();
    factory(Category::class, 10)->create();
    factory(Post::class, 100)->make()->each(function ($post) 
        $post->user()->associate(User::inRandomOrder()->first());
        $post->category()->associate(Category::inRandomOrder()->first());
        $post->save();
    );

另外User::all()->random(1) 将返回一个集合而不是模型,这会引发异常,我将其替换为Model::inRandomOrder()->first(),它将从数据库中获取随机模型。

来自docs:

当更新一个 belongsTo 关系时,你可以使用 associate 方法。此方法将在子模型上设置外键:

$account = App\Account::find(10);

$user->account()->associate($account);

$user->save();

更新

在您的模型工厂中:

/** @var Factory $factory */
$factory->define(Post::class, function (Faker\Generator $faker) 
    return [
        // not sure why you do this, is it not a autoincrement column?
        'id' => $faker->unique()->randomNumber(3), 
        'name' => $faker->realText($maxNbChars = 200, $indexSize = 2),
        'category_id' => function () 
            if ($category = Category::inRandomOrder()->first()) 
                return $category->id;
            

            return factory(Category::class)->create()->id;
        ,
        'user_id' => function () 
            if ($user = User::inRandomOrder()->first()) 
                return $user->id;
            

            return factory(User::class)->create()->id;
        ,
    ];
);

在您的播种机中:

public function seed()

    factory(User::class, 10)->create();
    factory(Category::class, 10)->create();
    factory(Post::class, 100)->create();

【讨论】:

感谢您的回答。不幸的是,我收到以下错误: 方法 Illuminate\Database\Eloquent\Collection::save 不存在。我尝试使用 create 而不是 save,但随后会导致错误提示 user_id 和 category_id 没有默认值。如果我将 'user_id' => 1 和 'category_id' => 1 插入工厂,它会创建完整性约束违规,尽管我尝试使用 DB::connection('test') 禁用外键检查 - >raw('SET UNIQUE_CHECKS = 0');和 Schema::connection('test')->disableForeignKeyConstraints(); @Loon 我更新了我的答案,它现在应该可以工作了,你必须在 each() 中调用 save ,而不是之后。我还添加了另一个解决方案,您可以在模型工厂中定义它。 抱歉回答晚了,我正忙于其他事情。无论如何,谢谢,它现在可以工作了!

以上是关于Laravel:种子表有几个属于使用工厂的关系的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.1 - 模型工厂错误种子

Laravel Dusk:迁移和种子测试数据库一次

从 Laravel 中的关系中检索关系

设计模式中的那些工厂

设计模式中的那些工厂

Laravel 4:处理种子中的关系