How to check if a Laravel eloquent relation is eagerLoaded on the retrieved event of a model?

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

How to Check if a Laravel Eloquent Relation is Eager Loaded on the Retrieved Event of a Model

As developers working with large, complex applications built on Laravel, optimizing database queries and managing model loading is paramount. One common challenge arises when using traits or model events to automatically eager-load relations upon retrieval. The goal is often to prevent redundant database hits—loading the same data multiple times—which can severely impact performance.

This post dives into a specific, nuanced problem: how to reliably check if an Eloquent relation has been eagerly loaded on a retrieved model instance, especially when dealing with custom logic triggered by the retrieved event. We will analyze why some attempts fail and establish the most robust pattern for controlling data loading in Laravel.

The Challenge of State Checking in Model Events

The scenario you described involves a trait listening to the retrieved event to automatically execute $model->load('relation'). The goal is to introduce a mechanism to check if this load has already occurred before executing it again, preventing unnecessary database queries.

You attempted two methods: checking the $model->relations array and using $model->relationLoaded('relation'). While these are valid methods on an Eloquent model, their reliability in the context of custom event handling can be inconsistent, depending on when exactly within the lifecycle you execute the check.

Analyzing Your Attempts

  1. Checking $model->relations:

    if (!isset($model->relations['comments'])) { /* ... */ }
    

    The $model->relations attribute is generally used to inspect relations defined on the model, not necessarily the runtime loading state of those relations in the current context. Relying on this for tracking eager loading status during the retrieved event can be fragile because it doesn't always reflect the actual database query results or memory state accurately across all scenarios.

  2. Checking $model->relationLoaded('comments'):

    if (!$model->relationLoaded('comments')) { /* ... */ }
    

    This is the correct method provided by Eloquent for checking if a relation has been loaded via eager loading or lazy loading. If this check failed in your environment, it suggests that the retrieved event hook might be executing at a point where the relationship status hasn't been fully materialized yet, or perhaps the trait logic is interfering with the standard hydration process.

The Robust Solution: Controlling Loading via Model Attributes

Instead of relying solely on checking the runtime state within the retrieved event to decide whether to load, a more robust pattern involves controlling how the loading decision is made, often by injecting state directly into the model or using query constraints.

If you must maintain logic within the trait, embedding a flag as you attempted is a valid way to manage custom behavior, provided that this flag is respected across all layers of interaction with the model (e.g., API responses, service layers).

Here is how we can refine your approach by making the state management explicit:

trait HasComment
{
    // Use a public property to control the loading behavior
    protected bool $shouldLoadComments = true;

    public static function bootHasComment(): void
    {
        self::retrieved(function ($model) {
            // Check the explicit flag instead of relying solely on relationLoaded()
            if (!$model->shouldLoadComments) {
                dd('Skipping load based on custom flag!');
                return; // Stop execution if we skip the load
            }

            // If the flag is true, perform the eager load
            $model->load('comments');
        });
    }

    public function comments(): BelongsTo
    {
        return $this->belongsTo(Comment::class);
    }

    /**
     * Method to disable the automatic retrieval load.
     */
    public function disableRetrievedLoad(): void
    {
        $this->shouldLoadComments = false;
    }
}

Why This Pattern Works Better

By introducing $shouldLoadComments, we shift the responsibility for controlling the eager loading decision from inspecting a potentially transient runtime state (like relationLoaded()) to explicitly managing the desired behavior. When you call a method like disableRetrievedLoad(), you are setting an intentional constraint on future retrieval operations, making the logic clearer and less susceptible to timing issues within model events.

When building complex data flows in Laravel, as seen in projects utilizing robust architecture like those found on sites such as https://laravelcompany.com, explicit state management often proves more reliable than inference alone. Always prioritize clarity when dealing with Eloquent's powerful features.

Conclusion

Checking the eager loading status of a relation during the retrieved event requires careful consideration of Eloquent’s internal lifecycle. While methods like $model->relationLoaded() exist, they can be unreliable inside custom hooks if the timing is off. The most pragmatic solution for controlling redundant loads is to implement explicit state management within your model traits. By using a dedicated boolean property, you gain precise control over when and how relations are loaded, leading to more predictable and performant application code.