Optimizing Laravel Eloquent Query Performance

A slow Laravel application is almost always caused by inefficient database queries, not by PHP itself. The good news is that Eloquent provides plenty of tools to make queries fast...

Optimizing Laravel Eloquent Query Performance

A slow Laravel application is almost always caused by inefficient database queries, not by PHP itself. The good news is that Eloquent provides plenty of tools to make queries fast — as long as you know how to use them. This article covers the highest-impact Eloquent optimization techniques, from the most common (N+1) to indexing and caching, with real code.

1. Fix the N+1 Problem with Eager Loading

This is the number-one cause of slowness. N+1 happens when you fetch a set of records and then access their relationship inside a loop:

$orders = Order::all();
foreach ($orders as $order) {
    echo $order->customer->name;   // 1 EXTRA query per iteration
}

For 500 orders that means 501 queries. Use with() to load the relationship at once:

$orders = Order::with('customer')->get();   // only 2 queries

You can also load nested relationships and limit their columns:

$orders = Order::with([
    'customer:id,name',
    'items.product:id,name,price',
])->get();

To stop N+1 from reaching production, enable automatic prevention in AppServiceProvider:

use Illuminate\Database\Eloquent\Model;

public function boot(): void
{
    Model::preventLazyLoading(! app()->isProduction());
}

2. Select Only the Columns You Need

By default Eloquent runs SELECT *. If a table has heavy columns like content or metadata, you waste memory and bandwidth. Fetch only what you need:

$users = User::select('id', 'name', 'email')->get();

If you only need a single column as an array, use pluck(), which is far leaner:

$emails = User::where('active', true)->pluck('email');

3. Process Large Data with chunk() and lazy()

Loading 100,000 rows with get() can exhaust memory. To process big data, fetch it in pieces:

User::chunk(500, function ($users) {
    foreach ($users as $user) {
        // process each user
    }
});

A more modern alternative is lazy(), which returns a LazyCollection so you can keep using a normal foreach without straining memory:

foreach (User::lazy() as $user) {
    // only a small slice of rows is in memory at any time
}

For mass updates, if you modify a column used as a condition, use chunkById() so you do not skip rows.

4. Add Database Indexes

Indexes are the most powerful way to speed up WHERE, ORDER BY, and JOIN queries. Columns you filter on often (like user_id, status, or email) should be indexed via a migration:

Schema::table('orders', function (Blueprint $table) {
    $table->index('status');
    $table->index(['user_id', 'created_at']); // composite index
});

Foreign keys should always be indexed. Without an index, the database must scan the entire table (a full table scan) for every query — very slow as data grows.

5. Cache Query Results That Rarely Change

Data like category lists or site settings rarely change but is fetched often. Store the result in cache so you do not hit the database repeatedly:

use Illuminate\Support\Facades\Cache;

$categories = Cache::remember('categories', 3600, function () {
    return Category::orderBy('name')->get();
});

remember() returns data from cache if present; otherwise it runs the query, stores it for 3600 seconds, and returns it. Remember to clear the cache when data changes with Cache::forget('categories').

6. Find Slow Queries

You cannot optimize what you do not measure. A quick way to monitor every query is DB::listen in AppServiceProvider:

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

DB::listen(function ($query) {
    if ($query->time > 100) {   // more than 100 ms
        Log::warning('Slow query', [
            'sql'  => $query->sql,
            'time' => $query->time,
        ]);
    }
});

For local development, install Laravel Debugbar, which shows the query count, duration, and duplicates right in the browser:

composer require barryvdh/laravel-debugbar --dev

Debugbar highlights N+1 problems instantly because you see the same query repeated many times.

7. Count Correctly

To count rows, never use count(User::all()), which loads all data into memory. Use a database aggregate:

$total = Order::where('status', 'paid')->count();

To check whether data exists, exists() is faster than count() > 0:

if (Order::where('user_id', $id)->exists()) {
    // ...
}

8. Push Heavy Calculations to the Database

Another common mistake is fetching many rows into PHP just to sum or average their values. Hand that work to the database, which is far more efficient at aggregation:

// Less efficient: loads all rows into memory
$total = Order::where('status', 'paid')->get()->sum('amount');

// Efficient: the database does the math
$total = Order::where('status', 'paid')->sum('amount');

The same applies to avg(), max(), and min(). For statistics that group data, use groupBy() at the query level so only the summarized result is sent to PHP. This can turn a query that loads thousands of rows into a single result row.

If you are counting related records, avoid loading the entire relationship just to count it. Use withCount():

$posts = Post::withCount('comments')->get();
// access via $post->comments_count without loading the comments

Conclusion

Optimizing Eloquent queries is not magic — it is measure, then fix. Your priorities: eliminate N+1 with eager loading, select only the columns you need, index frequently filtered columns, process large data with chunk/lazy, cache data that rarely changes, and push aggregation to the database. With Debugbar or DB::listen as your eyes, a once-heavy Laravel app can become light and responsive. Apply these one by one, measure the impact, and you will see response times drop dramatically.

Yudhi
Written by
Yudhi
Founder & Lead Developer, GudangCode

Yudhi is the founder of GudangCode and a Laravel developer who has built dozens of ready-to-use business information systems — from POS and HRIS to management apps. He writes guides and articles on GudangCode to help Indonesian developers run, understand, and deploy Laravel source code correctly.

LaravelPHPMySQLSistem Informasi Bisnis See all articles by Yudhi
Want the full source code & apps?

Sign up free to download ready-to-use business applications, information systems, and Laravel source code.

Sign Up Free & Download
Laravel Eloquent Performa Optimasi Database
Share this article
Back to Blog
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →

Get Full Access Now!

Join our membership and unlock exclusive access to all premium features. Fast, easy, and ready to use instantly.

Join Membership Now
Tim Support
Online
Isi data dulu untuk mulai chat:
Beri rating & testimoni sebelum menutup:
Live chat by gudangcode.com