Search code examples
laraveleloquentlaravel-9laravel-factory

Laravel 9 factory and creating related 1:1 models


I'd like to use Laravel factories to populate some dummy data in my test environment. I have a Locations model and a LocationDetails model, which have a 1:1 relationship and a record needs to be created in the LocationDetails table whenever a new Location is created.

How can I do this in a factory such that there's exactly 1 LocationDetails record for each Location record.

Here's what I have in my DatabaseSeeder class:

    if(env('APP_ENV') === 'local') {
        Client::factory()->count(25)->create();
        Location::factory()->count(30)->create();
    }

My location factory definition:

public function definition()
    {
        return [
            'name' => $this->faker->company,
            'client_id' => Client::all()->random()->id,
            'location_status_id' => LocationStatus::all()->random()->id,
            'address' => $this->faker->streetAddress,
            'city' => $this->faker->city,
            'state' => $this->faker->StateAbbr,
            'zip_code' => $this->faker->numerify('#####'),
            'phone' => $this->faker->numerify('###-###-####'),
            'email' => $this->faker->safeEmail,
            'is_onboarding_completed' => 1,
        ];
    }

It looks like Laravel has an afterCreate() callback for a factory, but I'm just not clear on how to accomplish this.


Solution

  • Does this work

    Location::factory()
        ->has(LocationDetails::factory())
        ->count(30)
        ->create();
    

    Or you can have a state defined on the LocationFactory

    //LocationFactory
    //Assuming location_id is the foreign key on LocationDetails model
    
    public function withLocationDetail()
    {
        return $this->afterCreating(function (Location $location) {
            LocationDetails::factory()->create(['location_id' => $location->id]);
        });
    }
    

    And then use it like

    Location::factory()
        ->withLocationDetail()
        ->count(30)
        ->create();