Laravel model factories with relation sharing foreign keys

luisgmoreno

Luigui Moreno

Posted on January 23, 2020

Laravel model factories with relation sharing foreign keys

Larevel users lets say you have three models User, Store and Product, and both Store and Product have a user_id foreign key to the users table, your Product factory may look like this:

<?php
//ProductFactory.php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Product;
use Faker\Generator as Faker;

$factory->define(Product::class, function (Faker $faker) {
    return [
        'name' => $faker->productName,
        'description' => $faker->text(),
        'price' => $faker->randomNumber(4),
        'sku' => $faker->uuid,
        'user_id' => function() {
            return factory(\App\User::class)->create()->id;
        },
        'store_id' => function() {
            return factory(\App\Store::class)->create()->id;
        }
    ];
});

And your StoreFactory like this:

<?php
//StoreFactory.php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Store;
use Faker\Generator as Faker;

$factory->define(Store::class, function (Faker $faker) {
    return [
        'name' => $faker->city,
        'location' => 'SRID=4326;POINT(-74.069891 4.605246)',
        'address' => $faker->address,
        'user_id' => function(){
            return create(\App\User::class)->id;
        },
        'code' => 'S' . $faker->randomNumber(5)
    ];
});

The problem is that then when generating products, the store they belong to doesn't belongs to the same user, and that makes no sense, these three models are just an example the same logic is valid for your specific database model, the solution is to access the current instance generated fields in the callback of the foreign key is:

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Product;
use Faker\Generator as Faker;

$factory->define(Product::class, function (Faker $faker) {
    return [
        'name' => $faker->productName,
        'description' => $faker->text(),
        'price' => $faker->randomNumber(4),
        'sku' => $faker->uuid,
        'user_id' => function() {
            return create(\App\User::class)->id;
        },
        'store_id' => function(array $product) {
            return factory(\App\Store::class)->create(['user_id' => $product['user_id']])->id;
        }
    ];
});

The callback argument array $product has the current instance fields, this way you can pass the dynamic generated User to the "sibling" Store model.

Hope it is useful!.

💖 💪 🙅 🚩
luisgmoreno
Luigui Moreno

Posted on January 23, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related