Sitemap
Alamgir Hosen

Alamgir Hosen

Full Stack Developer working with PHP, Laravel, Vue, and Nuxt. Learning DevOps.

Laravel Eloquent ORM, Part 6: Performance Optimization

3 min readSep 18, 2026
Press enter or click to view image in full size

Welcome to Part 6 of the Laravel Eloquent ORM series. Part 5 fixed the N+1 problem. Now let’s look at a few other common performance traps, each shown as a real scenario you’re likely to run into, along with the fix.

Scenario 1: Processing a Huge Table Crashes Your Server
The situation: You need to update or export 100,000 user records. You write this:

$users = User::all();

foreach ($users as $user) {
// process each user
}

The problem: all() loads every single row into memory at once. With a large table, this can exhaust your server's memory and crash the script.

The fix: Use chunk() to process records in small batches instead of all at once.

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

Now Eloquent fetches 500 records at a time, processes them, then moves to the next batch. Memory usage stays flat no matter how large the table grows.

Scenario 2: A Query Is Slow Even With Few Results
The situation: You run User::where('email', $email)->first(), and it takes several seconds, even though it only returns one row.

The problem: There’s likely no database index on the email column, so the database scans every single row to find a match.

The fix: Add an index in your migration.

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

This isn’t an Eloquent method, it’s a database-level fix, but it’s often the single biggest performance win available. Any column you frequently search, filter, or join on is a good index candidate.

Get Alamgir Hosen’s stories in your inbox

Join Medium for free to get updates from this writer.

Scenario 3: You’re Fetching More Data Than You Need
The situation: A page only needs to display post titles in a list, but the query loads every column, including large text or JSON fields.

Post::all(); // fetches every column, every row

The fix: Select only the columns you actually need.

Post::select(['id', 'title'])->get();

Less data pulled from the database, less data transferred, less memory used on your end. This matters even more once relationships are involved, as shown back in Part 5 with with('user:id,name').

Scenario 4: The Same Expensive Query Runs on Every Page Load
The situation: Your site shows a list of categories in the navigation menu, on every single page. That’s a database query on every request, even though categories rarely change.

The fix: Cache the result.

$categories = Cache::remember('categories', now()->addHours(6), function () {
return Category::all();
});

Now the query runs once every six hours instead of on every page load. The cache serves everyone else in between.

Putting It Together
A scheduled command that processes all active users, selecting only what’s needed, in safe batches:

User::where('is_active', true)
->select(['id', 'email'])
->chunk(500, function ($users) {
foreach ($users as $user) {
// send a notification, generate a report row, etc.
}
});

Filtered, minimal columns, chunked. This is the difference between a background job that finishes in seconds and one that times out or crashes.

What’s Next
We’ve now covered the most common performance traps and their fixes. In Part 7, we move into Advanced Eloquent Patterns: traits, macros, and custom collections.

Read the previous parts:
Part 1: Getting Started with Eloquent
Part 2: Relationships I (One-to-One and One-to-Many)
Part 3: Relationships II (Many-to-Many and Polymorphic)
Part 4: Query Scopes and Mutators
Part 5: Eager Loading

--

--

Alamgir Hosen
Alamgir Hosen

Written by Alamgir Hosen

Full Stack Developer working with PHP, Laravel, Vue, and Nuxt. Learning DevOps.