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.";
});
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
Or in jobs:
User::chunkById(100, function ($users) {
foreach ($users as $user) {
// process in small batches
}
});
Step 3: Eliminating the N+1 Query Problem
Here’s the classic mistake:
$users = User::all();
foreach ($users as $user) {
echo $user->posts->count();
}
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();
}
Now it’s 2 queries total.
Need just counts? Even better:
$users = User::withCount('posts')->get();
foreach ($users as $user) {
echo $user->posts_count;
}
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();
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();
That loads every comment into memory just to count them.
Do it right:
$count = Comment::where('post_id', $id)->count();
Or just check existence:
$hasComments = Comment::where('post_id', $id)->exists();
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'
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');
});
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();
});
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();
}
That’s query hell.
Do this instead:
$users = User::withCount('posts')
->with(['posts' => function ($q) {
$q->latest()->take(1);
}])
->latest()
->take(50)
->get();
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);
});
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();
}
- 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;
}
- 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)