Reputation: 91
tl;dr:
I am using Laravel and Eloquent and trying to seed a database and attach several belongs to relationship to one table, but I get the error:
Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsTo::attach()
Detailed explanation:
I am using php 7.1
and Laravel 6
with Eloquent. I am trying to seed a test database. I have the following relationship structure:
one user has several posts / one post belongs to one user
one category has several posts / one post belongs to one category
User model:
class User extends Model
{
protected $table = 'users';
public function posts()
{
return $this->hasMany('App\Models\Post');
}
}
Category model:
class Category extends Model
{
protected $table = 'categories';
public function posts()
{
return $this->hasMany('App\Models\Category');
}
}
Post model:
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');
}
}
User factory:
$factory->define(User::class, function (Faker $faker) {
return [
'id' => $faker->unique()->randomNumber(3),
'name' => $faker->name(),
];
});
Category factory:
$factory->define(Category::class, function (Faker $faker) {
return [
'id' => $faker->unique()->randomNumber(3),
'name' => $faker->word(),
];
});
Post factory:
$factory->define(User::class, function (Faker $faker) {
return [
'id' => $faker->unique()->randomNumber(3),
'name' => $faker->realText($maxNbChars = 200, $indexSize = 2),
];
});
If I only had users and posts, I know that I could seed the database the following way:
public function seed()
{
$users = factory(App\User::class, 10)
->create()
->each(function ($user) {
$user->posts()->createMany(factory(App\Post::class, 10)->make()->toArray());
});
}
but obviously that won't work in my case. I have tried the following:
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));
});
}
as well as the same function using make/save instead of 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();
}
but in both cases, I get the error:
Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsTo::attach()
Upvotes: 2
Views: 4758
Reputation: 8252
You have to use 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();
});
}
Also User::all()->random(1)
will return a collection and not a model, which will throw an exception, I replaced it with Model::inRandomOrder()->first()
, which will fetch a random model from the database.
From the docs:
When updating a belongsTo relationship, you may use the associate method. This method will set the foreign key on the child model:
$account = App\Account::find(10);
$user->account()->associate($account);
$user->save();
Update
In your Modelfactory:
/** @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;
},
];
});
In your seeder:
public function seed()
{
factory(User::class, 10)->create();
factory(Category::class, 10)->create();
factory(Post::class, 100)->create();
}
Upvotes: 6