{"id":315,"date":"2025-08-27T19:38:24","date_gmt":"2025-08-27T19:38:24","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=315"},"modified":"2025-08-27T19:38:26","modified_gmt":"2025-08-27T19:38:26","slug":"eager-loading-vs-lazy-loading-in-laravel-best-practices","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/eager-loading-vs-lazy-loading-in-laravel-best-practices\/","title":{"rendered":"Eager Loading vs Lazy Loading in Laravel: Best Practices"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Eager Loading vs Lazy Loading in Laravel: Best Practices<\/strong><\/h2>\n\n\n\n<p>When working with Eloquent relationships, performance issues often appear because of the \u201cN+1\u201d query problem. This happens when each record triggers additional queries for related data. Laravel provides <strong>lazy loading<\/strong> and <strong>eager loading<\/strong> to control how related data is fetched. In this article, you\u2019ll learn the difference between them, see code examples, and discover best practices for balancing performance and memory usage. We\u2019ll also build a simple profile UI that uses eager loading to display related posts efficiently.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 &#8211; Understanding Lazy Loading<\/strong><\/h2>\n\n\n\n<p><strong>Lazy loading<\/strong> means related data is only loaded when you first access it. This keeps initial queries lightweight but can trigger many additional queries if you loop through related data.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/UserController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UserController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        $users = User::all(); <span class=\"hljs-comment\">\/\/ single query for users<\/span>\n\n        <span class=\"hljs-keyword\">foreach<\/span> ($users <span class=\"hljs-keyword\">as<\/span> $user) {\n            <span class=\"hljs-comment\">\/\/ Each call triggers a separate query (N+1 problem)<\/span>\n            $posts = $user-&gt;posts;\n        }\n\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'users.index'<\/span>, compact(<span class=\"hljs-string\">'users'<\/span>));\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here, <code>User::all()<\/code> fetches all users. But every time <code>$user-&gt;posts<\/code> is accessed in the loop, Laravel issues another query, leading to dozens or hundreds of queries\u2014this is the <em>N+1 problem<\/em>.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 &#8211; Using Eager Loading<\/strong><\/h2>\n\n\n\n<p><strong>Eager loading<\/strong> solves the N+1 problem by fetching related data in advance with one extra query. You use <code>with()<\/code> to tell Eloquent which relations to load.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/UserController.php (improved)<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UserController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-comment\">\/\/ One query for users + one query for posts<\/span>\n        $users = User::with(<span class=\"hljs-string\">'posts'<\/span>)-&gt;get();\n\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'users.index'<\/span>, compact(<span class=\"hljs-string\">'users'<\/span>));\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Instead of dozens of queries, Laravel now runs two: one for users and one for posts. Relations are hydrated into each user automatically, eliminating N+1 overhead.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 &#8211; Nested Eager Loading<\/strong><\/h2>\n\n\n\n<p>You can eager load nested relations by passing arrays to <code>with()<\/code>. Useful for dashboards that display multi-level data.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Fetch users with posts and each post's comments<\/span>\n$users = User::with(&#91;<span class=\"hljs-string\">'posts.comments'<\/span>])-&gt;get();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This runs three queries: one for users, one for posts, and one for comments. Eloquent maps everything together in memory, saving you from manually stitching queries.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 &#8211; Eager Loading with Constraints<\/strong><\/h2>\n\n\n\n<p>Use a closure inside <code>with()<\/code> to filter related data. This prevents loading unnecessary rows.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Only load published posts for each user<\/span>\n$users = User::with(&#91;<span class=\"hljs-string\">'posts'<\/span> =&gt; <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">($q)<\/span> <\/span>{\n    $q-&gt;where(<span class=\"hljs-string\">'status'<\/span>, <span class=\"hljs-string\">'published'<\/span>);\n}])-&gt;get();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Even though a user might have many posts in different statuses, only published ones are pulled into memory. This keeps responses small and efficient.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Lazy Eager Loading (N+1 Fix After the Fact)<\/strong><\/h2>\n\n\n\n<p>If you\u2019ve already loaded a collection without eager loading, you can still fix N+1 by calling <code>load()<\/code> after the fact.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$users = User::all();        <span class=\"hljs-comment\">\/\/ only users<\/span>\n$users-&gt;load(<span class=\"hljs-string\">'posts'<\/span>);       <span class=\"hljs-comment\">\/\/ now posts are fetched in one extra query<\/span>\n\n<span class=\"hljs-keyword\">foreach<\/span> ($users <span class=\"hljs-keyword\">as<\/span> $user) {\n    <span class=\"hljs-comment\">\/\/ no extra queries here<\/span>\n    <span class=\"hljs-keyword\">foreach<\/span> ($user-&gt;posts <span class=\"hljs-keyword\">as<\/span> $post) {\n        <span class=\"hljs-keyword\">echo<\/span> $post-&gt;title;\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>load()<\/code> attaches the related models after the initial query, solving the N+1 problem if you forgot to use <code>with()<\/code> upfront.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 &#8211; A Simple Profile UI Example<\/strong><\/h2>\n\n\n\n<p>Let\u2019s build a profile page that shows the user\u2019s information and their posts. We\u2019ll use eager loading to prevent N+1 queries.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/ProfileController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ProfileController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">show<\/span><span class=\"hljs-params\">($id)<\/span>\n    <\/span>{\n        $user = User::with(<span class=\"hljs-string\">'posts'<\/span>)-&gt;findOrFail($id);\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'profile.show'<\/span>, compact(<span class=\"hljs-string\">'user'<\/span>));\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This controller uses <code>with('posts')<\/code> so both the user and their posts are loaded in only two queries.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/profile\/show.blade.php --&gt;\n@extends(<span class=\"hljs-string\">'layouts.app'<\/span>)\n\n@section(<span class=\"hljs-string\">'content'<\/span>)\n&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">container<\/span>\"&gt;\n  &lt;<span class=\"hljs-title\">h1<\/span>&gt;<\/span>{{ $user-&gt;name }}\u2019s Profile&lt;\/h1&gt;\n  &lt;p <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">text<\/span>-<span class=\"hljs-title\">muted<\/span>\"&gt;<span class=\"hljs-title\">Email<\/span>: <\/span>{{ $user-&gt;email }}&lt;\/p&gt;\n\n  &lt;h3 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">mt<\/span>-5 <span class=\"hljs-title\">mb<\/span>-3\"&gt;<span class=\"hljs-title\">Posts<\/span>&lt;\/<span class=\"hljs-title\">h3<\/span>&gt;\n  @<span class=\"hljs-title\">foreach<\/span>($<span class=\"hljs-title\">user<\/span>-&gt;<span class=\"hljs-title\">posts<\/span> <span class=\"hljs-title\">as<\/span> $<span class=\"hljs-title\">post<\/span>)\n    &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span> <span class=\"hljs-title\">mb<\/span>-3\"&gt;\n      &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">body<\/span>\"&gt;\n        &lt;<span class=\"hljs-title\">h5<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">title<\/span>\"&gt;<\/span>{{ $post-&gt;title }}&lt;\/h5&gt;\n        &lt;p <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">text<\/span>\"&gt;<\/span>{{ Str::limit($post-&gt;body,<span class=\"hljs-number\">120<\/span>) }}&lt;\/p&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  @<span class=\"hljs-keyword\">endforeach<\/span>\n&lt;\/div&gt;\n@endsection<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The Blade file prints user info and loops through their posts. Because we eager-loaded the posts in the controller, this page will always use a constant two queries, no matter how many posts exist.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Lazy loading is simple but can cause N+1 queries in loops. Eager loading (<code>with()<\/code>) fetches related data in bulk and avoids performance pitfalls. Lazy eager loading (<code>load()<\/code>) is handy when you already have a collection but need extra relations. By choosing the right loading method, you keep queries efficient and your app fast\u2014even with complex relationships.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/advanced-eloquent-relationships-tips-and-tricks\">Advanced Eloquent Relationships: Tips and Tricks<\/a><\/li>\n<li><a href=\"\/blog\/how-to-use-eloquent-api-resources-for-clean-apis\">How to Use Eloquent API Resources for Clean APIs<\/a><\/li>\n<li><a href=\"\/blog\/query-performance-tuning-in-laravel-mysql\">Query Performance Tuning in Laravel + MySQL<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Eager Loading vs Lazy Loading in Laravel: Best Practices When working with Eloquent relationships, performance issues often appear because of the \u201cN+1\u201d query problem. This happens when each record triggers additional queries for related data. Laravel provides lazy loading and eager loading to control how related data is fetched. In this article, you\u2019ll learn the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":319,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[36,44,37],"class_list":["post-315","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-eloquent","tag-performance","tag-relationships"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/315","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=315"}],"version-history":[{"count":2,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/315\/revisions"}],"predecessor-version":[{"id":320,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/315\/revisions\/320"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/319"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=315"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=315"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=315"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}