.../articles/
Improving Laravel Factory Inserts to Speed Up Tests

Improving Laravel Factory Inserts to Speed Up Tests

2019.12.24

Environment

  • Laravel 5.x

Laravel's factory is powerful — it lets you create hierarchical data with intuitive syntax. However, it issues one insert per record, so it needs a bit of care.

// Create 5 users
factory(User::class, 3)
->create()
->each(function (User $user) {
    // Create 5 orders
    factory(Order::class, 5)
    ->create(['user_id' => $user->id])
    ->each(function (Order $order) {
        // Create 10 order_details
        factory(OrderDetail::class, 10);
    });
});

This code ends up issuing 3×5×10 = 150 inserts, which isn't great for performance. In cases like this, factory has a make function that only generates instances, which we can combine with insert.

// Create 3 users
$users = factory(User::class, 3)->make();
User::query()->insert($users->toArray());
// Find the users we just inserted
$users = User::query()->orderBy('created', 'desc')->limit(3)->get(); 
$users->each(function (User $user){

    // Create 5 orders
    $orders = factory(Order::class, 5)->make(['user_id' => $user->id]);
    Order::query()->insert($orders->toArray());
    // Find the orders we just inserted
    $orders = Order::query()->orderBy('created', 'desc')->limit(5)->get(); 

    $orders->each(function(Order $order){
        // Create 10 order_details
        $orderDetails = factory(OrderDetail::class, 10)->make([
            'user_id' => $user->id, 'order_id' => $order->id
        ]);
        OrderDetail::query()->insert($orderDetails->toArray());
    });
});

It's frustrating that this makes the code harder to follow, but it reduced the number of SQL executions from 150 down to 23 (1+1+3*(2+5*(1))).

After I wrote the code above, I got a code review comment saying "this way would bring it down even further." With the code below, the total number of SQL executions comes down to 5.

<?php 

// Create 3 users
$users = factory(User::class, 3)->make();
User::query()->insert($users->toArray());
$users = User::query()
    ->orderBy('created', 'desc')
    ->limit(3)
    ->get(); 

// Create the orders associated with the users
$orders = [];
$users->each(function (User $user){
    $orders[] = factory(Order::class, 5)
        ->make(['user_id' => $user->id])
        ->toArray();
});
Order::query()->insert($orders);

// Fetch the orders we just created (linked to the users),
// then create the order_details linked to those orders
$orderDetails = []
$orders = Order::query()
    ->whereIn('user_id', $users->pluck('user_id')
    ->toArray())
    ->get();
$orders->each(function(Order $order){
    $orderDetails[] = factory(OrderDetail::class, 10)
        ->make(['user_id' => $user->id, 'order_id' => $order->id])
        ->toArray());
});
OrderDetail::query()->insert($orderDetails);

You can also use the make function when you want to insert data you've already prepared as an array, by passing it through a factory.

$vegetables = [
    ['vegetable_group' => 1, 'name' => 'daikon radish'],
    ['vegetable_group' => 1, 'name' => 'pumpkin'],
    ['vegetable_group' => 1, 'name' => 'sweet potato'],
    ['vegetable_group' => 2, 'name' => 'garlic chives'],
    ['vegetable_group' => 2, 'name' => 'lettuce'],
    ...
    ...
    ['vegetable_group' => 9, 'name' => 'enoki mushroom'],
    ['vegetable_group' => 9, 'name' => 'shimeji mushroom'],
];

$insertVegetables = [];
foreach($vegetables as $v){
    $insertVegetables[] = factory(Vegetables::class)->make($v)->toArray();
}
Vegetables::query()->insert($insertVegetables);

Closing Thoughts

We had a project where the test suite was taking a long time to run, and wanting to improve that, I tuned it and wrote up what I did in this article. I'd be curious to know how larger projects design their test data.

written by

.../article/

Articles

All articles

From Firebase to Vercel, Contentful to microCMS — a migration log written with Claude Code

We moved our corporate site's hosting and CMS, and made it bilingual along the way. The constraints we only found by running against real data were more useful than the migration itself, so this post focuses on where we got stuck.

2026.08.05

Naoki Ioroi

Can't Read POST Data with Firebase Functions × Remix?

How to read POST data from a Remix action when running on Firebase Functions.

2025.05.20

Naoki Ioroi

Generative AI for Executives and Leaders: An Approach to Self-Driven DX

Building a structure where executives and leaders themselves can identify issues and evaluate solutions using generative AI. We introduce how combining this with our hands-on support dramatically improves both the quality and speed of digital transformation.

2025.05.06

Naoki Ioroi

Deploying a Monorepo Next.js App (App Router) to AWS Amplify

Notes on the obstacles we hit while deploying a Next.js app managed in a monorepo to AWS Amplify.

2023.12.09

Naoki Ioroi

Keeping Production Running Smoothly with Remote Work and Online Meetings [Documentation]

Many production companies have adopted remote work as a result of the pandemic, and we are one of them.

2022.09.20

Ryoya Chihara

Designing an E-Commerce Site That Sells: How to Find Great Reference Examples

There is no single formula for e-commerce design that sells. Driving revenue requires a solid concept, and getting to that concept requires thorough research.

2022.09.13

Ryoya Chihara

Productivity Tools We Recommend as a Production Company, Including Services That Work Well Solo

With remote work becoming the norm during the COVID-19 pandemic, our team now works from home most days of the week.

2022.09.12

Ryoya Chihara

We Released Thought Recorder, a Figma Plugin for Keeping a Commit History of Your Designs

We hope this helps web designers who work in Figma. Read on for how to use it.

2022.09.11

Kanae Akagishi

How to Build an E-Commerce Site, and Which Platforms We Recommend

Shopping online for fashion, appliances, and even groceries is now routine. With the pandemic accelerating the shift, we receive a steady stream of questions about which platform to use and how much it costs.

2022.07.14

Ryoya Chihara

Generating FastAPI Schema Classes from OpenAPI

We chose FastAPI, a relatively modern framework, for a Python API project. FastAPI can generate an OpenAPI definition from your backend code, but here we do the opposite: generating FastAPI schema classes from an OpenAPI definition prepared in advance.

2022.03.04

Nobuhiro Onodera

View all articles

Recruit

We're hiring teammates to build this with us

We're currently hiring for the following roles

  • Backend engineer
  • Frontend engineer
  • Web engineer intern
View open roles