Laravel 8: Seed Tables with Relationships Easily
Master Laravel seeding with relationships using this detailed guide for efficient database setup.
Laravel provides the feature to seed your database with dummy data that can be used while testing.
Having a single seeder class for single/simple models is very easy. But in the real-world application, the models might be associated with each other in different Relationships.
For database seeding, Laravel uses the fzaninotto/faker seeder package.
All the seed classes are stored in the database/seeders directory. We have a DatabaseSeeder class defined as default. Using the call method from this class you can run other seed classes which allows you to control the seeding order.
So, how can we seed tables with relationships in Laravel 8? Let’s learn how to define relationships on the Eloquent model and use Laravel’s model factory to seed the database.
Install Laravel
We will install a fresh Laravel 8 (we are working with 8.26.1 version) project to use database seeding with relationships. We will create relationships between tables and then insert the dummy data.
To create a new Laravel project, run the below command:
composer create-project laravel/laravel seeder-demo --prefer-dist
Once the installation is completed, open the project in your favorite code editor.
MySql Database Configuration
Open the .env file and update the database configuration.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=seeder-demo
DB_USERNAME=root
DB_PASSWORD=password
Tables Creation
Laravel comes with few default migrations. So let's run them first by executing the below command:
php artisan migrate

As you can see in the image, we have a users table created after running the above command. So we will create the rest of the tables: posts and comments.
php artisan make:migration create_posts_table
php artisan make:migration create_comments_table

Now, we need to update our migration files to define the table columns. We don’t need to make any changes to the migration file of the users table as it has columns already defined.
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
Open the migration file of the posts table and update it with the below code:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
}
Open the migration file of the comments table and update it with the below code:
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->string('comment');
$table->unsignedBigInteger('post_id');
$table->foreign('post_id')->references('id')->on('posts');
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
$table->timestamps();
});
}
Run the migrations again to create new tables with defined fields.
php artisan migrate

Model, Controller, Seeder Creation
We have a User.php already created, so we will create an only controller and seeder class for the User model, and for Post, Comment models we have to create all three classes.
For User Model:
php artisan make:Controller UserController
php artisan make:seeder UserSeeder

You can generate all these three classes using the single command as well.
For Post Model:
php artisan make:model Post -cs
For Comment Model:
php artisan make:model Comment -cs

Factory Class Creation
We will create a factory class for Post and Comment models only as we have a User factory class already available.
php artisan make:factory PostFactory --model="App\\Post"
php artisan make:factory CommentFactory --model="App\\Comment"

Models Relationship
We are going to define a relationship between three models: User, Post, and Comment. Consider an application in which:
- A User has multiple Post(s) > A Post belongs to a User
- A Post has multiple Comment(s) > A Comment belongs to a Post

Add below code in respective model files.
User Model:
/**
* Get the posts for the user.
*/
public function posts()
{
return $this->hasMany(Post::class);
}
Post Model:
/**
* Get the user that owns the post.
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany(Comment::class);
}
Comment Model:
/**
* Get the post that owns the comment.
*/
public function post(){
return $this->belongsTo(Post::class);
}
Now we have a relationship set up between these three models. Let’s update our factory class to define a dummy data schema.
Update Factory Classes
Add below code in the respective model’s factory class files.
PostFactory
public function definition()
{
return [
'title' => $this->faker->sentence(5),
'user_id' => User::factory(1)->create()->first(),
];
}
CommentFactory
public function definition()
{
return [
'comment' => $this->faker->paragraph(2),
'user_id' => User::factory(1)->create()->first(),
'post_id' => Post::factory(1)->create()->first(),
];
}
Update Seeder Classes
Update the seeder classes with the below content.
UserSeeder
public function run()
{
User::factory(5)->create();
}
PostSeeder
public function run()
{
Post::factory(10)->create();
}
CommentSeeder
public function run()
{
Comment::factory(20)->create();
}
Run Seeder Classes
You can run the seeder class separately using the below commands:
php artisan db:seed --class=UserSeeder
php artisan db:seed --class=PostSeeder
php artisan db:seed --class=CommentSeeder

As you can see above, all three seeders ran successfully and inserted the related data into three tables!! Wow…
But, we have a problem here… :)
Problem:
At first glance, it seems working but if you check the database tables, you will find that extra records are being created than written in seeder classes.
Reason:
We have written code in factory classes so that they can be executed independently and to allow this we have written code to generate a new related entity model record each time.
Check below the code of PostFactory and CommentFactory classes
public function definition()
{
return [
'title' => $this->faker->sentence(5),
'user_id' => User::factory(1)->create()->first(),
];
}
public function definition()
{
return [
'comment' => $this->faker->paragraph(2),
'user_id' => User::factory(1)->create()->first(),
'post_id' => Post::factory(1)->create()->first(),
];
}
- Each time when a single post is created, it’s also creating a new record of a User in order to map with the
user_idfield. - Each time when a single comment is created, it’s also creating new records of a User and a Post in order to map with
user_idandpost_idfields.
Solution:
As all three models are related to each other, their seeders should not be executed separately. They should be executed in a specific order.
Make changes in factory classes as below:
public function definition()
{
return [
'title' => $this->faker->sentence(5),
'user_id' => rand(1, User::count())
];
}
public function definition()
{
return [
'comment' => $this->faker->paragraph(2),
'user_id' => rand(1, User::count()),
'post_id' => rand(1, Post::count())
];
}
Run the below command to run fresh migration
php artisan migrate:fresh

And run the seeder in a specific order (UserSeeder, PostSeeder, CommentSeeder)

If you check in the database you can see we have the exact numbers of records inserted that we defined in factory classes:
- 5 Users
- 10 Posts
- 20 Comments
Improvement:
The above solution is working as expected, but we have to take care of the order of execution when running the seeder commands. If you remember, in the starting section of this article we have written below lines
All the seed classes are stored in the database/seeders directory. We have a DatabaseSeeder class defined as default. Using the call method from this class you can run other seed classes which allows you to control the seeding order.
Open DatabaseSeeder class file and update with below code:
public function run()
{
$this->call([
UserSeeder::class,
PostSeeder::class,
CommentSeeder::class,
]);
}
After making the above changes run the below command again:
php artisan migrate:fresh
Now, we don’t need to run three seeders separately or manually maintain their order. We just need to run a single seed command:
php artisan db:seed

As you can see, it's working and inserted the exact records we have defined!!
I hope this article will help you to work with seeder having relationships defined between models in Laravel.
0 Comments