Skip to main content
Tutorial Free

Laravel Seeding: HasMany with Multiple Levels

October 11, 2022
2 min read

When using seeders, the data is often interconnected: we want to seed some data with related records. Is there a "quick" syntax to do it?

If we take a look at Laravel docs, we can find this example:

$user = User::factory()
->has(Post::factory()->count(3))
->create();

This example assumes that the relationship is User -> hasMany -> Posts.

What this example doesn't cover, is that you can chain this deeper, more than one level. So, let's imagine the scenario of this relationship:

Customer -> hasMany -> Order
Order -> hasMany -> Product
Product -> hasMany -> Ingredient

And we need to seed the data for Customers, each with X orders, each order with Y products, and each product with Z ingredients.

If we have the Factory classes for those Models ready, we can write this short chain in the CustomerSeeder or even the DatabaseSeeder file:

public function run()
{
Customer::factory(100)
->has(Order::factory(2))
->has(Product::factory(3))
->has(Ingredient::factory(5))
->create();
}

This will create 100 customers, 2 orders for each customer, 3 products for each order, and 5 ingredients for each product.

Looks like a beautiful one-liner, doesn't it?

You can read more about Laravel factories and seeding in the Laravel docs here.

Enjoyed This Tutorial?

Get access to all premium tutorials, video and text courses, and exclusive Laravel resources. Join our community of 10,000+ developers.

Comments & Discussion

J
Joe ✓ Link copied!

This works really well, but I was wondering, is there a way to randomise the number of orders, products and ingredients being created each time, in order to make the seed data a bit more realistic?

I tried to do ->has(Product::factory(rand(1,3))) hoping that it would create between 1 and 3 products for each order but acutally it seems as though it only evaluates the random number once so all of the orders have 1, 2 or 3 products depending on the number it chose.

I know I can achieve this by using a for loop or even each() but then we loose a lot of the beauty of your example.

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.