Eager loading in an ORM is one of the easiest ways to fix the N+1 query problem in PHP applications. If your page loads a list of models and then quietly fires extra queries for every related record, eager loading is usually the first thing to check.
This post explains how eager loading works, why it matters, and when it helps more than it hurts.
What eager loading means
In ORM terms, eager loading means fetching related data up front instead of waiting until each relationship is accessed later. The main goal is to avoid the N+1 query pattern where one query becomes dozens or hundreds.
That matters in PHP because repeated database round trips usually cost more than the neatness of the code is worth.
A simple example without eager loading
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $pdo->query('SELECT * FROM users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($users as $user) {
$stmt = $pdo->prepare('SELECT * FROM posts WHERE user_id = ?');
$stmt->execute([$user['id']]);
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($posts as $post) {
echo $post['title'] . PHP_EOL;
}
}
This looks harmless until the user list gets large. One query for users plus one query per user becomes expensive quickly.
Eager loading with batched related data
$stmt = $pdo->query('SELECT * FROM users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query('SELECT * FROM posts');
$posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
$postsByUser = [];
foreach ($posts as $post) {
$postsByUser[$post['user_id']][] = $post;
}
foreach ($users as $user) {
$userPosts = $postsByUser[$user['id']] ?? [];
foreach ($userPosts as $post) {
echo $post['title'] . PHP_EOL;
}
}
Now you are down to two queries total. That is the core idea behind eager loading even when the ORM hides the implementation details.
Eager loading with a join
$stmt = $pdo->query('
SELECT users.id AS user_id, users.name AS user_name,
posts.id AS post_id, posts.title AS post_title
FROM users
LEFT JOIN posts ON posts.user_id = users.id
ORDER BY users.id
');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
A join can work well when the data shape is straightforward and you want fewer queries with predictable output. The right choice depends on your ORM, your relationship size, and how the result is consumed.
When eager loading helps most
- Lists that always render related data, like users with posts or orders with items
- API responses that return nested relationships
- Admin screens that paginate relational data
- Dashboards where repeated relationship queries dominate the page cost
When not to use eager loading
Eager loading is not automatically correct. It can create bigger queries, higher memory usage, and unnecessary data transfer when the related data is optional or rarely used.
- You do not need the relationship on most requests
- The related collection is huge
- You are loading deep nested relationships just in case
This is where people swing too far. The right answer is not “always eager load.” The right answer is “load what the page actually needs.”
Eager loading and pagination
One of the easiest mistakes is eager loading a huge relationship set and then wondering why the page still feels heavy. Pagination still matters. Load a sensible number of parent rows, then eager load only what those rows need.
If you need a refresher on the pagination side, read how to build PHP MySQL pagination. If the whole request is still slow, work through the broader PHP performance pitfalls too.
Final take
Understanding eager loading is less about memorizing ORM syntax and more about understanding query cost. Once you see what the N+1 problem does to a real page, it becomes much easier to choose the right loading strategy and keep your PHP app fast.

