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

pgogy's avatar

Seeders and foreign key relationships

I’m using a model (application) which has two hasOne models (applicant and referrer) linked to it.

So the application table has applicant_id and referrer_id and both the linked to tables have application_id

In the seeder I create the application, but how do I pass the ID of the application to the factories for referrer and applicant?

I’m very new here, so assume I’m wrong / ignorant

Thanks all

0 likes
14 replies
pgogy's avatar

The application has a referrer_id - how do I populate that?

Glukinho's avatar

Please show your database migration and relations definition of models.

1 like
drgreen's avatar

I'm assuming you're designing a system where an Applicant can submit one or many Application and a Referrer can refer many Application.

  • An Application belongs to an Applicant and a Referrer.
  • An Applicant has many Application or an Applicant has one Application, depending on your requirements.
  • A Referrer has many Application.
class Application extends Model
{
    public function applicant() {
        return $this->belongsTo(Applicant::class);
    }

    public function referrer() {
        return $this->belongsTo(Referrer::class);
    }
}
class Applicant extends Model
{
    // if applicant can submit multiple applications
    public function applications() {
        return $this->hasMany(Application::class);
    }

    // or if you want to limit to 1 application
    public function application() {
        return $this->hasOne(Application::class);
    }
}
class Referrer extends Model
{
    public function applications() {
        return $this->hasMany(Application::class);
    }
}

Then you can do something like this:

Application::factory()
    ->for(Applicant::factory())
    ->for(Referrer::factory())
    ->create();

// or if you already have existing `$applicant` and `$referrer` instances:

Application::factory()
    ->state([
        'applicant_id' => $applicant->id,
        'referrer_id' => $referrer->id,
    ])
    ->create();

Or, you can also define the relationships in the Application factory:

public function definition(): array
{
    return [
        'applicant_id' => Applicant::factory(),
        'referrer_id' => Referrer::factory(),
        ...
    ];
}
Tray2's avatar

You really shouldn't use seeders for anything complex like this, they are usually only used for testing, and in some cases used to enter static data that is needed for the application.

If it is for testing you can use factories to create relations like that.

Here is an example

// In the UserFactory, create posts using the "posts" relationship method
$user = User::factory()
    ->has(Post::factory()->count(3), 'posts')
    ->create();
pgogy's avatar

Hi all,

Lots to reply to

Models

public function Applicant(){
        return $this->hasOne( Applicant::class );
    }

and in the applicant

public function Application() {
        return $this->belongsTo( Application::class );
    }

in the migration


Schema::create('applicants', function (Blueprint $table) {
            $table->id();
            $table->foreignIdFor(Application::class);
            $table->string('title');
            $table->string('first_name');
            $table->string('last_name');
});

Schema::table('applications', function (Blueprint $table) {
            $table->foreignIdFor( Applicant::class );#
});

So the applications table has an applicant_id, and the applicant table has an application_id

My application factory is

return [
            'applicant_id' => Applicant::factory()
        ];

My applicant factory is

return [
            'title' => $this->faker->title(),
            'application_id' => ????,
            'first_name' => $this->faker->firstName(),
            'last_name' => $this->faker->lastName()
];

But I can't work out what to put in the applicant factory where ???? is.

If I put a Application::Factory() it'll get stuck in a loop.

I'd like to maintain the relationship?

I worked out a method where I could use Model::count() +1 in the applicant factory to relate to original application, but that feels like a work around and there must be a better way

martinbean's avatar

@pgogy I think you’ve over-complicated the problem. Wouldn’t “applicant” and “referrer” both be users or some other person-like entity? If so, you can just define two relations pointing to the same model, then:

Schema::create('applications', function (Blueprint $table) {
    $table->id();
    $table->foreignId('applicant_id')->constrained('users')->cascadeOnDelete();
    $table->foreignId('referrer_id')->constrained('users')->cascadeOnDelete();
    // Other application table columns...
});

You’d then define these relationships on your Application model:

class Application extends Model
{
    public function applicant(): BelongsTo
    {
        return $this->belongsTo(User::class, 'applicant_id');
    }

    public function referrer(): BelongsTo
    {
        return $this->belongsTo(User::class, 'referrer_id');
    }
}

And the inverse on your User model:

class User extends Authenticatable
{
    // ...

    public function applications(): HasMany
    {
        return $this->hasMany(Application::class, 'applicant_id');
    }

    public function referrals(): HasMany
    {
        return $this->hasMany(Application::class, 'referrer_id');
    }
}

Seeding would then be easy, so long as you set up your ApplicationFactory properly:

class ApplicationFactory extends Factory
{
    public function definition(): array
    {
        return [
            'applicant_id' => User::factory(),
            'referrer_id' => User::factory(),
            // Other application-related fields...
        ];
    }
}
class ApplicationSeeder extends Seeder
{
    public function run(): void
    {
        // Automatically create application model with applicant and referrer...
        Application::factory()->create();
    }
}
pgogy's avatar

@martinbean Thank you

They are people, but they have very different attributes, and only the referrer will ever log in, applicant does not (these are the words the people I am working for use, not my choices). Referrer may change organisation and so the record needs to reflect their position at the time of application.

I'm just a bit lost because I think there's some core stuff I am either missing or expecting laravel to do for me. I've been looking for four days or so now just for a basic example (I find the docs very light on information). All my issues seem to be coming from the foreign keys. If I have a foreign key in applications pointing to applicants (and vice versa) my view works (I can use application->applicant). If I drop the foreign key in applications, the factory works but the view fails (applicant becomes null).

Reading from here https://laravel.com/docs/12.x/eloquent-relationships#one-to-one

Additionally, Eloquent assumes that the foreign key should have a value matching the primary key column of the parent. In other words, Eloquent will look for the value of the user's id column in the user_id column of the Phone record.

So I assume I do need a foreign key per table, but that seems to mess up seeders (and now i'm trying to make create work and that's giving me the not null error).

if you could point me to some code with a basic working hasOne in it i'd be able to work from there

martinbean's avatar
Level 80

@pgogy I think your issue is stemming from you thinking you need to put foreign keys in both tables. You don’t. It’s impossible to define a cyclical relationship like that as you can’t insert one record without pointing to the other, and that other record can’t be created without the initial record being created first.

So if you are wanting to create individual tables and models for applicants and referrers then do so, and put the foreign keys in your applications table:

Schema::create('applications', function (Blueprint $table) {
    $table->id();
    $table->foreignId('applicant_id')->constrained()->cascadeOnDelete();
    $table->foreignId('referrer_id')->constrained()->cascadeOnDelete();
    // Other application table columns...
});
Schema::create('applicants', function (Blueprint $table) {
    $table->id();
    // Applicant-specific columns...
});
Schema::create('referrers', function (Blueprint $table) {
    $table->id();
    // Referrer-specific columns...
});

Then define your model relations:

class Application extends Model
{
    public function applicant(): BelongsTo
    {
        return $this->belongsTo(Applicant::class);
    }

    public function referrer(): BelongsTo
    {
        return $this->belongsTo(Referrer::class);
    }
}
class Applicant extends Model
{
    public function applications(): HasMany
    {
        return $this->hasMany(Application::class);
    }
}
class Referrer extends Model
{
    public function applications(): HasMany
    {
        return $this->hasMany(Application::class);
    }
}

Like my previous answer, you’d then be able to use your ApplicationFactory to create application models in a seeder:

class ApplicationFactory extends Factory
{
    public function definition(): array
    {
        return [
            'applicant_id' => Applicant::factory(),
            'referrer_id' => Referrer::factory(),
        ];
    }
}
class ApplicantSeeder extends Seeder
{
    public function run(): void
    {
        Application::factory()->create();
    }
}
1 like
pgogy's avatar

Sorry @martinbean I just worked this out. My SQL is very rusty and I guess I overdesigned it. Thank you so much for your patience and guidance. I am very grateful. Please let me know a charity i can donate too.

1 like
pgogy's avatar

Think I have fixed it, I had gone wrong in making two foreign keys

Please or to participate in this conversation.