DEV Community

Japheth Joepari
Japheth Joepari

Posted on

Laravel Eloquent on Steroids: From 3s to 300ms

Ever been in a situation where your Laravel dashboard should have loaded instantly but took more than six seconds to load?

Your client questions why they hired you in the first place, the page freezes, and your laptop's fan sounds like it's ready to take off.

I’ve been there. I nearly lost a client when I shipped a dashboard that crawled at eight seconds.

That’s when I realized: performance is survival, not optional.

Let’s fix this.


Step 1: Stop Pretending You Know — Measure It

You can’t fix what you don’t measure. Guessing is for gamblers, not devs. Add a timer and see how long your request actually takes.

Route::get('/test', function () {
    $start = microtime(true);  

    $users = \App\Models\User::all();  

    $time = microtime(true) - $start;  
    return "Query took {$time} seconds and loaded " . count($users) . " users.";  
});
Enter fullscreen mode Exit fullscreen mode

If you just saw 3.452312 seconds — congrats, you’re the proud owner of a slow query. At least now you know.


Step 2: Replace all() With Smarter Data Retrieval

One of the biggest performance traps in Laravel is dropping Model::all() everywhere.

all() means: “Give me literally everything.” Every row. Every column.

Got 50k users? You just told Laravel to load 50k models into memory. I once crashed a staging server doing this “just for testing.”

Fix it with pagination:

$users = User::paginate(20); // safe, predictable
Enter fullscreen mode Exit fullscreen mode

Or in jobs:

User::chunkById(100, function ($users) {
    foreach ($users as $user) {
        // process in small batches
    }
});
Enter fullscreen mode Exit fullscreen mode

Step 3: Eliminating the N+1 Query Problem

Here’s the classic mistake:

$users = User::all();  

foreach ($users as $user) {
    echo $user->posts->count();
}
Enter fullscreen mode Exit fullscreen mode

Looks fine, but this is the infamous N+1 problem.

  • 1 query for users
  • 1 query for every user’s posts
  • 100 users = 101 queries

Fix it with eager loading:

$users = User::with('posts')->get();

foreach ($users as $user) {
    echo $user->posts->count();  
}
Enter fullscreen mode Exit fullscreen mode

Now it’s 2 queries total.

Need just counts? Even better:

$users = User::withCount('posts')->get();

foreach ($users as $user) {
    echo $user->posts_count;
}
Enter fullscreen mode Exit fullscreen mode

That’s how you go from 500 queries → 2.


Step 4: Stop Carrying the Whole Database in Your Backpack

Why fetch bio, avatar, reset token, and grandma’s last_login_at if you only want the name?

$users = User::select('id', 'name')->get();
Enter fullscreen mode Exit fullscreen mode

Smaller result sets = faster responses.


Step 5: Count in the Database, Not in PHP

I’ve seen this:

$post = Post::find($id);
$count = $post->comments->count();
Enter fullscreen mode Exit fullscreen mode

That loads every comment into memory just to count them.

Do it right:

$count = Comment::where('post_id', $id)->count();
Enter fullscreen mode Exit fullscreen mode

Or just check existence:

$hasComments = Comment::where('post_id', $id)->exists();
Enter fullscreen mode Exit fullscreen mode

Fast. Efficient. Doesn’t kill memory.


Step 6: Index Your Damn Columns

If your table has 2M rows and you keep querying:

WHERE email = 'bob@example.com'
Enter fullscreen mode Exit fullscreen mode

with no index, the DB scans every row like an unpaid intern flipping files.

Add indexes in your migrations:

Schema::table('users', function (Blueprint $table) {
    $table->index('email');
});
Enter fullscreen mode Exit fullscreen mode

I once forgot to index a busy status column. It slowed production down. After adding the index, queries went from 2s → 20ms.


Step 7: Cache the Heavy Stuff

Your homepage recalculates “Top 10 Most Commented Posts” on every request? That’s cruel.

use Illuminate\Support\Facades\Cache;

$topPosts = Cache::remember('top_posts', 60, function () {
    return Post::withCount('comments')
        ->orderByDesc('comments_count')
        ->take(10)
        ->get();
});
Enter fullscreen mode Exit fullscreen mode

One query per minute instead of per request. Users don’t care if it’s 60s old — they care if it’s fast.


Step 8: Stop Writing Heavy Loops Like an Amateur

This garbage:

$users = User::all();

foreach ($users as $user) {
    $latest = $user->posts()->latest()->first();
    $count = $user->posts()->count();
}
Enter fullscreen mode Exit fullscreen mode

That’s query hell.

Do this instead:

$users = User::withCount('posts')
    ->with(['posts' => function ($q) {
        $q->latest()->take(1);
    }])
    ->latest()
    ->take(50)
    ->get();
Enter fullscreen mode Exit fullscreen mode

One query for users, one for posts. Done.


Step 9: Spy on Your Queries

Curious what your app is really doing? Put Laravel under surveillance:

DB::listen(function ($query) {
    dump($query->sql, $query->bindings, $query->time);
});
Enter fullscreen mode Exit fullscreen mode

Hit the page. If you see 200 queries flying out, you’ve found the problem.


Real-World Before & After

Before (painful):

$users = User::all();

foreach ($users as $user) {
    echo $user->posts->count();
}
Enter fullscreen mode Exit fullscreen mode
  • 101 queries
  • 3 seconds
  • Client asking if this is built on WordPress

After (fast):

$users = User::withCount('posts')->latest()->take(50)->get();

foreach ($users as $user) {
    echo $user->posts_count;
}
Enter fullscreen mode Exit fullscreen mode
  • 2 queries
  • 300ms
  • Client actually pays invoice

The Blunt Conclusion

Laravel’s Eloquent is awesome, but if you treat it like a toy, it will slow down your Production.

The difference between a 3s page and a 300ms page usually comes down to:

  • Don’t abuse all()
  • Kill N+1 with with()
  • Select only the columns you need
  • Let the DB do the heavy lifting
  • Add indexes where they matter
  • Cache what doesn’t need to be real-time
  • Actually inspect your queries

Do this, and you’ll stop getting Slack messages that say:

“Site’s down again.”

Don’t be the dev who ships a dashboard that loads slower than a YouTube ad. Happy Coding.

Top comments (0)