Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jimmyhowedotcom's avatar

Model Factory: Referencing a foreign key

How would I reference a 'foreign key' on another table, eg. have the 'user_id' field on the Post factory use the 'id' field on the User table?

$factory->define(App\User::class, function ($faker) {
    return [
        'username' => $faker->userName,
        'email' => $faker->email,
        'password' => str_random(10),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Post::class, function ($faker) {
    return [
        'user_id' => 'factory:App\User',
        'title' => $faker->sentence,
        'body' => $faker->paragraph
    ];
});
0 likes
17 replies
Stricken's avatar

Try:

$factory->define(App\Post::class, function ($faker) use ($factory)  {
    return [
        'user_id' => $factory->create(App\User::class)->id,
        'title' => $faker->sentence,
        'body' => $faker->paragraph
    ];
});
6 likes
taraqr9's avatar

'user_id' => User::all()->random()->id (UPDATE)

5 likes
jimmyhowedotcom's avatar

With this, my UsersTableSeeder creates 20 rows in the users table, then the PostsTableSeeder creates 20 posts, but also creates a user for each post created... so I end up with 40 Users, the last 20 have have posts assigned... is there a way to just have 20 Users and Posts, with the posts 'category_id' assigned to a user?

RachidLaasri's avatar

You can do it with 2 different ways :

1 - If you always truncate the users tables before seeding again then just use

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->sentence,
        'body' => $faker->paragraph,
        'user_id' => rand(1,20)
    ];
});

2 - If not, You can use the lists method and grab the users IDs from the database and do same as above.

1 like
eukaryoter's avatar

Otherwise there's another solution where you use the factory you should be able to override the randoms with an ID of a user.

This is cut from the documentation maybe it should work?

Adding Relations To Models

You may even persist multiple models to the database. In this example, we'll even attach a relation to the created models. When using the create method to create multiple models, an Eloquent collection instance is returned, allowing you to use any of the convenient functions provided by the collection, such as each:

$users = factory('App\User', 3)
           ->create()
           ->each(function($u) {
                $u->posts()->save(factory('App\Post')->make());
            });
5 likes
prithvi's avatar

Try this:

// ModelFactory
$factory->define(App\User::class, function ($faker) {
    return [
        'username' => $faker->userName,
        'email' => $faker->email,
        'password' => str_random(10),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Post::class, function ($faker) {
    return [
        // 'user_id' => 'factory:App\User',
    // pass the 'user_id' as argument in your seeder
        'title' => $faker->sentence,
        'body' => $faker->paragraph
    ];
});


// Seeder
factory(App\User::class, 20)->create()->each(
    function($user) {
        factory(App\Post::class)->create(['user_id' => $user->id]);
    }
);
8 likes
EventFellows's avatar

I had the same problem but could not solve it the way @RachidLaasri described it as my IDs in the user table continue to increase with every new seeding (I call 'delete' instead of 'truncate' before the seeding - 'truncate' did not work due to foreign keys being used.

Here is how I made it to work: --> first seed the user table --> query the user table for min and max User ID from within the faker setup (I do random assignment)

$factory->define(App\Credit::class, function (Faker\Generator $faker) {
    return [
        'credit_type' => 'Subscription',
        'validity' => '1 month',
        'user_id' => random_int(\DB::table('users')->min('id'), \DB::table('users')->max('id')),
    ];
});
6 likes
jdforsythe's avatar

I think this is a better way - pick a random id from existing ids...

$factory->define(App\Credit::class, function(Faker\Generator $faker) {
    $user_ids = \DB::table('users')->select('id')->get();
    $user_id = $faker->randomElement($user_ids)->id;

    return [
      'user_id' => $user_id,
      // ...
    ];
});
12 likes
OpenSS's avatar

I was trying to find a nice way to get create objects that contained pre-made foreign keys and this is what I ended up with. Region is the foreign object which has already been created.

$factory->define(Business::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->company,
        'region_id' => Region::all()->random()->id,
        'turnover' => $faker->numberBetween(100000, 1000000),
        'description' => $faker->paragraph(),
    ];
});

The magic is in using the random() function of the Eloquent collection:

'region_id' => Region::all()->random()->id
26 likes
barnabas.kecskes's avatar

I had the same problem on seeder classes. For above example, it would look something like this:

$factory->define(App\User::class, function ($faker) {
    return [
        'username' => $faker->userName,
        'email' => $faker->email,
        'password' => str_random(10),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Post::class, function ($faker) {
    return [
        'user_id' => function() { return factory(App\User::class)->id },
        'title' => $faker->sentence,
        'body' => $faker->paragraph
    ];
});

...and in my PostsTableSeeder class, it would look like this:

User::all()->each(function($user) {
    $user->posts()->saveMany(
        factory(Post::class, 5)->make(['user_id'=>$user->id)
    )
});

This has solved the problem for me, but you need user_id to be specified on make, otherwise it won't work.

Rogercbe's avatar

Might be late to the party but I often go this way about it, either get me the first User or create one if there isn't.

$factory->define(App\Post::class, function ($faker) {
        return [
            'user_id' => function () {
                return App\User::first()->id ?: factory(App\User::class)->create()->id;
            },
            'title' => $faker->sentence,
            'body' => $faker->paragraph
        ];
    });
1 like
ciptohadi's avatar

I just wanna share. I have already users and threads created. Then I randomly assign existing users and threads to a new reply

$factory->define(App\Reply::class, function (Faker $faker) {
    $user_ids = App\User::pluck('id');
    $thread_ids = App\Thread::pluck('id');

    return [
        'body' => $faker->paragraph,
        'user_id' => $user_ids->random(),
        'thread_id' => $thread_ids->random(),
    ];
});
2 likes
eXist73's avatar

@ciptohadi Thank you sir. I took this approach and turned it into one liners:

    return [
        'email' => $faker->unique()->safeEmail,
        'password' => $password ?: $password = bcrypt('secret'),
        'remember_token' => str_random(10),
        'first_name' => $faker->firstName,
        'last_name' => $faker->lastName,
        'street' => $faker->streetAddress,
        'city' => $faker->city,
        'state_province_id' => App\StateProvince::pluck('id')->random(),
        'zip' => $faker->postcode,
        'country_id' => App\Country::pluck('id')->random(),
        'timezone' => $faker->timezone,
        'system_of_measurement_id' => App\SystemOfMeasurement::pluck('id')->random(),
        'currency_id' => App\Currency::pluck('id')->random(),
        'phone' => $faker->phoneNumber,
        'language_id' => App\Language::pluck('id')->random(),
        'image' => null
    ];
1 like
guizo's avatar

Adaptation of @OpenSS answer:

$factory->define(App\User::class, function ($faker) {
    return [
        'username' => $faker->userName,
        'email' => $faker->email,
        'password' => str_random(10),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Post::class, function ($faker) {
    return [
        'user_id' => App\User::all(['id'])->random(),
        'title' => $faker->sentence,
        'body' => $faker->paragraph
    ];
});
3 likes
KarimLaravelMaster's avatar

You can do this :

  • if you always truncate the users tables before seeding

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->sentence,
        'body' => $faker->paragraph,
        'user_id' => rand(1,\App\Models\User::all()->count())
    ];
});
Fratello's avatar

$user = App\Models\User::pluck('id'); return [

        'user_id'=> $this->faker->randomElement($user),
    ];

Please or to participate in this conversation.