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):
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.
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
Let's start.
hasContacts()
refers to hasMany relation define in Model. In model we use smallcase method exactly likecontacts()
but in factory/seeder we use firstlater capital withhas
keyword.What do we have to do differently to seed the database with real data?
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.