How to lazy load a custom attribute on a Laravel model?

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

How to Lazy Load a Custom Attribute on a Laravel Model Without $appends

The desire to lazy load custom attributes on an Eloquent model is a common one for developers moving beyond simple one-to-one relationships. We want the performance benefits of only loading data when explicitly requested, similar to how we handle Eloquent relationships using methods like load() or eager loading. However, Laravel's default mechanisms—specifically accessors and $appends—don't natively provide a clean, built-in way to achieve this for arbitrary custom properties.

This post will explore why the standard approaches fall short and present a more robust, developer-centric pattern for achieving true lazy loading of model attributes in Laravel.

The Limitations of Standard Eloquent Loading

You correctly identified the core conflict: you want dynamic loading without the overhead of $appends (which loads everything upfront) or the performance/cleanliness issues of manually assigning properties ($model->foo = $model->foo).

When you define an accessor method, like this:

public function getFooAttribute(): string
{
    return 'bar';
}

Every time you access $model->foo, the accessor executes. If you want to defer this execution until a specific load operation, we need to intervene in how Eloquent loads data from the database into the model instance. There is no built-in loadAttribute() method for arbitrary attributes like there is for relationships.

The Recommended Solution: Customizing Model Loading

Since Eloquent provides powerful tools for managing data retrieval, the best way to introduce custom lazy loading behavior is by extending or manipulating the base loading mechanism. Instead of trying to change how accessors work, we should focus on changing what data gets loaded when a model instance is retrieved from the database.

A highly effective pattern involves creating a custom scope or leveraging the query builder functionality to inject this loading logic. However, for true instance-level lazy loading, we can define a helper method that forces the retrieval of the attribute only when called, mimicking the desired behavior.

Implementing a Custom Lazy Loader

We can implement a helper method on the model itself that checks if the data has been loaded and, if not, fetches it from the database only for that specific attribute. This approach keeps the accessor clean while adding the necessary lazy-loading layer.

Here is an example demonstrating how you might structure this:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;

class MyModel extends Model
{
    // Define the custom attribute accessor (still exists for easy access)
    public function getFooAttribute(): string
    {
        // This method will now be called only when explicitly requested via loadFoo()
        return $this->foo_value ?? $this->loadFoo();
    }

    /**
     * Custom lazy loading method for the 'foo' attribute.
     */
    public function loadFoo(): string
    {
        // Check if the attribute is already loaded (if we were storing it on the model)
        // For true database-backed lazy loading, we query the DB directly or via relations.
        $value = DB::table(get_class($this))->where('id', $this->id)->value('foo');

        if ($value === null) {
            // If not found, update the model instance (or use a dedicated property if preferred)
            $this->attributes['foo'] = $value ?? 'default_value';
        }
        return $this->attributes['foo'];
    }
}

The Practical Application: Using Collection Manipulation

While the above example handles an instance-level load, achieving a true $model->loadAttribute('foo') across an entire collection requires a slightly different approach focused on controlling the data set returned by Eloquent. For operations involving Model::all(), you can use collection methods to iterate and trigger these lazy loads if necessary, though this generally shifts the complexity towards explicit iteration rather than pure magic.

A cleaner alternative, especially when dealing with complex loading patterns, is to define relationship-like methods that handle the fetching logic internally. As discussed in documentation related to Eloquent design principles found on sites like https://laravelcompany.com, focusing on well-defined relationships often leads to more performant and maintainable code than attempting to force arbitrary attribute lazy loading directly into the core model mechanism.

Conclusion

We cannot replicate the exact $model->loadAttribute('foo') syntax natively within Eloquent without modifying the framework itself. However, by implementing custom methods that check for loaded state and trigger database queries only when necessary—as demonstrated above—we achieve the goal of lazy loading: deferring expensive data retrieval until it is explicitly requested. This pattern gives you fine-grained control over performance while maintaining clean accessors for convenience.