Seed a pivot table using factories in Laravel
Asked Answered
S

5

13

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' => ..?
    ];
});
Souvenir answered 2/10, 2016 at 16:33 Comment(0)
P
18

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,
    ]
);
Phonetic answered 2/10, 2016 at 16:55 Comment(1)
But this way you need to create a Model UserSkill for the pivot table user_skill? And this is not necessary right? There is no way to seed that sable just taking the current id's of the users and skills tables?Souvenir
P
17

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

Piker answered 24/10, 2020 at 7:22 Comment(1)
Any ideas how to do this for a polymorphic relationship? Eg. commentable()Gesualdo
E
8

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

Database

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      |   |         |            |             |
+----------------+   +----------------------+-------------+

User.php

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

DataBaseTest.php

<?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);
    }
}
Einkorn answered 11/3, 2019 at 15:57 Comment(0)
C
1

Seed Models and pivot table using factories:

Tested using Laravel 10.6 with PHP 8.1 , assuming you have your factories all set for User & Role Models.

1. Many to Many :

$roles= Role::factory(3)->create(); 
$users= User::factory(3)->hasAttached($roles)->create();

It creates:

  • 3 Roles
  • 3 Users with every user attached to the 3 roles created
  • 9 role_user record

NB: use loop to get as much as u want

2. One to One placeholder one-liner :

User::factory(20)->has(Role::factory())->create();

It creates:

  • 20 Users
  • 20 Roles
  • 20 role_user records (one-to-one user_id = role_id)
Confucius answered 26/4, 2023 at 23:58 Comment(0)
P
0

This worked under the following conditions:

  • laravel vesrion 11.16.0
  • php v8.3
  • mysql v8.0
  1. The UserFactory and UserSeeder classes are created as usual.
  2. The SkillFactory is created as follows:
class SkillFactory extends Factory
{
    public function definition(): array
    {
        return [
            'skill' => $this->faker->unique()->word()
        ];
    }

}
  1. The 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();
    }
}
  1. in 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;');
        }
    }
}
Petrolatum answered 25/8 at 10:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.