Laravel / Cannot access protected property Illuminate\Database\Eloquent\Collection::$items

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Solving Nested Eloquent Queries: How to Access Deeply Nested Relationships Efficiently

As a senior developer working with the Laravel ecosystem, you frequently encounter scenarios where fetching deeply nested data using Eloquent relationships seems straightforward but ends up causing performance bottlenecks or tricky collection issues. This is especially true when dealing with multi-level associations like the one you described: Users belong to Circles, Circles contain Repositories, and Repositories contain Items.

The problem you are facing—where you can retrieve the repositories but fail to grab all associated items correctly—is a classic symptom of how Eloquent handles lazy loading versus bulk retrieval. Let's dive into why this happens and how we can fix it using the most efficient technique: Eager Loading.

The Pitfall of Lazy Loading in Nested Relations

When you access relationships without explicitly instructing Eloquent to load them upfront, it relies on lazy loading. This means that when you iterate over a collection, Eloquent executes separate database queries for each relationship lookup.

In your original setup:

  1. You fetch the Circle for the user.
  2. You fetch the repositories for that circle (Query 1).
  3. When you loop through $repositories and access $repository->items, Eloquent runs a new query for every single repository to fetch its items.

This results in an N+1 query problem, where $N$ is the number of repositories. While this works fine for simple one-to-one or one-to-many relations, when dealing with many-to-many or deep nesting, this approach becomes extremely inefficient and slow, especially under heavy load. This pattern defeats the purpose of leveraging Laravel’s expressive Eloquent querying capabilities.

The Solution: Mastering Nested Eager Loading

The solution is to instruct Eloquent to fetch all required related data in a single, optimized batch of queries using Eager Loading. For deeply nested relationships, we use nested with() clauses. This tells the database exactly what you need so that Laravel can retrieve all necessary data in the fewest possible round trips.

To solve your specific issue, we need to load the repositories and then, within those repositories, eagerly load their respective items.

Implementing Nested Eager Loading

Instead of letting Eloquent lazily load the items inside your loop, we will modify the initial query in your controller to pre-load everything.

Here is how you would refactor your controller logic:

function library() {
    $user = Auth::user();

    // Use nested with() to eagerly load all required relationships at once
    $user = $user->load('circle.repositories.items');

    // Now, accessing the data is fast and complete
    $circle = $user->circle;
    $repositories = $circle->repositories;
    $items = $repositories->flatMap(fn ($repo) => $repo->items); // A clean way to flatten results if needed

    return View('pages.library', compact(['user', 'circle', 'items']));
}

Explanation of the Fix:

The key is the load() method (or chaining multiple with() calls). By specifying ->load('circle.repositories.items'), you instruct Eloquent to perform a single, highly optimized query (often involving joins or subqueries) that fetches the user, their circle, all associated repositories, and subsequently, all items belonging to those repositories.

When you access $repositories->items afterwards, the data is already loaded into memory, eliminating the performance hit of individual database calls. This approach aligns perfectly with the philosophy of building performant applications in Laravel, as championed by the team at laravelcompany.com.

Conclusion: Efficiency Through Eager Loading

Understanding the difference between lazy loading and eager loading is fundamental to writing high-performance Laravel applications. When dealing with complex relational data, especially nested structures like your user-circle-repository-item hierarchy, always default to eager loading. By using nested with() or load() calls, you transform potentially slow, iterative queries into a single, efficient database operation. Embrace eager loading; it is the cornerstone of writing clean, fast, and scalable code in Laravel.