Reputation: 362
I'm new to Laravel and looking for a good way to seed a pivot table using factories. I don't want to use plain seeders. I'll show you the case:
I have three tables (users, skills, and user_skill).
users user_skill skills
+----------------+ +----------------------+ +-----------------+
| id | name | | user_id | section_id | | id | skills |
+----------------+ +----------------------+ +-----------------+
| 1 | Alex | | | | | 1 | draw |
|----------------| |----------------------| |-----------------|
| 2 | Lucy | | | | | 2 | program |
|----------------| |----------------------| |-----------------|
| 3 | Max | | | | | 3 | social |
|----------------| |----------------------| +-----------------+
| 4 | Sam | | | |
+----------------+ +----------------------+
Is there a good way to take reals Id
's of the Users table and reals Id
's of the Skills table to seed the pivot table? I want to do it randomly, but I don't want random numbers that don't match any ID. I want the ID to match the users and skills.
I don't know how to start, and I'm looking for a good example. Maybe something like this?
$factory->defineAs(App\User::class, 'userSkills', function ($faker) {
return [
'user_id' => ..?
'skills_id' => ..?
];
});
Upvotes: 13
Views: 16348
Reputation: 1220
This worked under the following conditions:
11.16.0
UserFactory
and UserSeeder
classes are created as usual.SkillFactory
is created as follows:class SkillFactory extends Factory
{
public function definition(): array
{
return [
'skill' => $this->faker->unique()->word()
];
}
}
SkillSeeder
class is created like this:class SkillSeeder extends Seeder
{
public function run(): void
{
Skill::truncate();
Skill::factory()
->hasAttached(
User::inRandomOrder()->take(3)->get('id'),
)
->count(10)
->create();
}
}
DatabaseSeeder
class:class DatabaseSeeder extends Seeder
{
public function run(): void
{
if(app()->environment('local')) {
// temporarily disable FK check
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
$this->call([
UserSeeder::class,
TagSeeder::class,
]);
// enable FK check
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
}
}
}
Upvotes: 0
Reputation: 383
Tested using Laravel 10.6 with PHP 8.1 , assuming you have your factories all set for User & Role Models.
$roles= Role::factory(3)->create();
$users= User::factory(3)->hasAttached($roles)->create();
NB: use loop to get as much as u want
User::factory(20)->has(Role::factory())->create();
Upvotes: 1
Reputation: 181
For those who are using laravel 8.x and are looking for a solution to a problem like this;
In laravel 8.x you can feed your pivot using Magic Methods, For example if you have a belongsToMany relation named "userSkills" in User model, you should feed the pivot table this way:
User::factory()->hasUserSkills(1, ['skills' => 'draw'])->create();
You can find the documentation here
Upvotes: 18
Reputation: 101
I had a similar problem and I resolved this way on Laravel testing.
Don't need to create a new UserSkills Model:
Version Laravel 5.7
users user_skill skills
+----------------+ +------------------------------------+ +-----------------+
| id | name | | user_id | section_id | state_skill | | id | skills |
+----------------+ +------------------------------------+ +-----------------+
| 1 | Alex | | | | | | 1 | draw |
|----------------| |----------------------|-------------| |-----------------|
| 2 | Lucy | | | | | | 2 | program |
|----------------| |----------------------|-------------| |-----------------|
| 3 | Max | | | | | | 3 | social |
|----------------| |----------------------|-------------| +-----------------+
| 4 | Sam | | | | |
+----------------+ +----------------------+-------------+
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Authenticatable
{
use Notifiable;
use SoftDeletes;
public function skills()
{
return $this->belongsToMany('App\Skill')
->withTimestamps()
->withPivot('state_skill');
}
}
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;
class DataBaseTest extends TestCase
{
/**
* @test
*/
public function test_create_user_skill()
{
// Create DataBase
$users = factory(\App\User::class, 1)
->create()
->each(function ($user) {
// Create Models Support
$skill = factory(\App\Skill::class)->create();
// Create Pivot with Parameters
$user->skills()->attach($skill->id,[
'state_skill' => 'ok'
]);
});
// Testing
// ...
$this->assertTrue(true);
}
}
Upvotes: 8
Reputation: 2601
I do not think that this is the best approach but it works for me.
$factory->define(App\UserSkill::class, function (Faker\Generator $faker) {
return [
'user_id' => factory(App\User::class)->create()->id,
'skill_id' => factory(App\Skill::class)->create()->id,
];
});
If you do not want to create a model just for the pivot table, you can insert it manually.
DB::table('user_skill')->insert(
[
'user_id' => factory(App\User::class)->create()->id,
'skill_id' => factory(App\Skill::class)->create()->id,
]
);
Or, with random existing values.
DB::table('user_skill')->insert(
[
'user_id' => User::select('id')->orderByRaw("RAND()")->first()->id,
'skill_id' => Skill::select('id')->orderByRaw("RAND()")->first()->id,
]
);
Upvotes: 18