Optimizing Laravel Eloquent Queries for Performance

Common Eloquent performance pitfalls and how to fix them, with before-and-after code and query counts.

The N+1 Query Problem

The single most common Laravel performance issue. It’s easy to write and easy to miss without checking query counts:

// N+1: fires one query per post to get its author
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name;
}

// Fixed: eager load the relationship
$posts = Post::with('author')->get();
foreach ($posts as $post) {
    echo $post->author->name;
}

Selecting Only What You Need

// Pulls every column, even ones you don't use
$users = User::all();

// Only pulls what's actually needed
$users = User::select('id', 'name', 'email')->get();

Using chunk() for Large Datasets

// Loads everything into memory at once — dangerous at scale
$users = User::all();

// Processes in manageable batches
User::chunk(500, function ($users) {
    foreach ($users as $user) {
        $user->sendReminderEmail();
    }
});

Avoiding Unnecessary Model Hydration

// Hydrates full Eloquent models just to check existence
if (User::where('email', $email)->first()) { ... }

// Cheaper: exists() runs a lighter query
if (User::where('email', $email)->exists()) { ... }

Counting Related Records Efficiently

// N+1 again: one COUNT query per post
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->comments->count();
}

// withCount does it in a single query
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
    echo $post->comments_count;
}

Debugging Query Counts

DB::enableQueryLog();
// ... run your code
dd(DB::getQueryLog());

Or use Laravel Debugbar / Telescope in local development to catch N+1 patterns visually before they reach production.

Caching Expensive Queries

$topProducts = Cache::remember('top-products', 3600, function () {
    return Product::orderByDesc('sales_count')->limit(10)->get();
});

Conclusion

Most Eloquent performance problems come from a small set of repeatable mistakes: missing eager loading, over-fetching columns, and re-querying data that could be cached. Check your query counts during development, not after a production slowdown forces the issue.