When I inherited a Laravel application that was struggling with performance
issues, the biggest bottleneck wasn't the code—it was how we were querying
the database.
The Problem
The application was loading user dashboards with related data: orders,
customers, products, and pricing. Each page load triggered over 100 database
queries, taking 3-5 seconds to render.
The Investigation
I used Laravel Debugbar to analyze the queries and found the classic N+1
problem. For every order, we were making separate queries for customer data,
product details, and pricing information.
The Solution
Step 1: Eager Loading
Instead of lazy loading relationships, I implemented strategic eager loading:
// Before: N+1 queries
$orders = Order::all();
foreach ($orders as $order) {
echo $order->customer->name; // New query each time!
}
// After: Optimized
$orders = Order::with(['customer', 'items.product'])->get();
Step 2: Query Scoping
I created reusable query scopes for common data patterns:
public function scopeWithFullDetails($query) {
return $query->with([
'customer.priceList',
'items.product.category',
'payments'
]);
}
Step 3: Strategic Caching
For frequently accessed, rarely changing data (like product catalogs),
I added Redis caching:
$products = Cache::remember('products.catalog', 3600, function () {
return Product::with('category')->get();
});
The Results
- Page load time: 3-5 seconds → under 500ms
- Database queries: 100+ → under 10 per page
- Server load reduced significantly
- Better user experience
Lessons Learned
- Always profile before optimizing - Don't guess where the bottleneck is
- Eager loading is your friend - But don't over-eager-load everything
- Cache wisely - Not everything needs real-time data
- Think in relationships - Design your queries around how data connects
Performance optimization isn't magic—it's systematic analysis and
thoughtful solutions.
Top comments (0)