Laravel: Two Ways to Seed Data with Relationships

While seeding data, it's common to have one class for each database table. But what if there are relationships? I will show you two ways you can deal with it. Imagine we have this simple database structure - database contacts and contact_companies (taken from our Contact Management module in QuickAdminPanel): contact management laravel How to seed data in both tables? Of course, we will use Faker library, but still - there are a few ways to implement it. First, let's define our factories. database/factories/ContactCompanyFactory.php:
$factory->define(App\ContactCompany::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'address' => $faker->address,
        'website' => 'https://' . $faker->word . '.com',
        'email' => $faker->email,
    ];
});
And database/factories/ContactFactory.php:
$factory->define(App\Contact::class, function (Faker\Generator $faker) {
    return [
        'first_name' => $faker->firstName(),
        'last_name' => $faker->lastName,
        'phone1' => $faker->phoneNumber,
        'phone2' => $faker->phoneNumber,
        'email' => $faker->email,
        'skype' => $faker->word,
        'address' => $faker->address,
    ];
});
Now, as you can see, there's no contacts.company_id defined. Here's where we have a few options:

Version 1. Create contacts inside of company seed

Let's create a company seeder:
php artisan make:seeder CompanySeed
And then fill the file database/seeds/CompanySeed.php with this:
public function run()
{
    factory(App\ContactCompany::class, 10)->create()->each(function ($company) {
        $company->contacts()->save(factory(App\Contact::class)->make());
    });
}
Basically, we're creating 10 companies, and for each of them we're creating one contact, using Eloquent relationship in app/ContactCompany.php:
public function contacts()
{
    return $this->hasMany(Contact::class, 'company_id');
}

Version 2. Create company along with the contact

The other way around would be to use seed for Contacts.
php artisan make:seeder ContactSeed
And then add only this line in database/seeds/ContactSeed.php:
public function run()
{
    factory(App\Contact::class, 10)->create();
}
You're probably wondering where the relationship would come from? We can create a "parent" entry directly in the factory! Like this: database/factories/ContactFactory.php:
$factory->define(App\Contact::class, function (Faker\Generator $faker) {
    return [
        'company_id' => factory('App\ContactCompany')->create()->id,
        'first_name' => $faker->firstName(),
        'last_name' => $faker->lastName,
        'phone1' => $faker->phoneNumber,
        'phone2' => $faker->phoneNumber,
        'email' => $faker->email,
        'skype' => $faker->word,
        'address' => $faker->address,
    ];
});
See the first field? We're using parent factory and creating a company "on-the-fly". That's it, isn't it simple? In official Laravel documentation you can find more information about seeding and using factories with Faker.
avatar

In Laravel 9 we need to chnage few things to work with Factory Relationships. Let's continue with Version 01 example.

In Model app/Models/ContactCompany.php

public function contacts()
{
    return $this->hasMany(Contact::class, 'company_id');
}

Let's start.

hasContacts() refers to hasMany relation define in Model. In model we use smallcase method exactly like contacts() but in factory/seeder we use firstlater capital with has keyword.

ContactCompany::factory()
    ->count(6)
    ->hasContact(2) // create two records/contact of each company
    ->create();


OR

ContactCompany::factory()
    ->count(6)
    ->has(Contact::factory()->count(3), 'contacts')
    ->create();


OR


ContactCompany::factory()
->hasContact(2, function (array $attributes, ContactCompany $contactCompany) {
    return ['contact_type' => $contactCompany->type];
})	
👍 2
avatar

What do we have to do differently to seed the database with real data?

avatar

It depends what do you mean by real data.

If it's from production? In that case, you should export/import the SQL.

If it's production like, then your factories need to be modified to be as close to truth as possible.

BUT, there is a catch:

Database copy from production should not really be used locally. It can, but you have to be really careful to never send any emails or trigger any API calls if you have them. Otherwise, you can mess up the workflows for customer.

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 66 courses (1167 lessons, total 43 h 18 min)
  • 88 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent New Courses