Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

emjayess's avatar

N+hundreds, using this Attribute Accessor approach?

When I use this earlier convention of a model attribute accessor:

use App\Services\LocationService;
use App\Models\Location;

//
    public function getDistanceToOfficeAttribute()
    {
        $office = Location::office()->first();

        return LocationService::distance(
            origin: $this->navigationAddress,
            destination: $office->navigationAddress
        );
    }

and then something like this in blade:

@php
    $cachedDistance = $user->distanceToOffice;
@endphp

<div>
    Distance to office: {{ $cachedDistance->distance }}les
    (about {{ $cachedDistance->duration['in_traffic'] }})
</div>

... the database query generated by Location::office()->first() fires twice, and I can observe this duplicate in the Laravel debugbar.

I'd like to prevent this duplicated database query, of course..

But here's where it gets wild and I don't understand why; when I opt in to a typed Attribute Accessor, like this one:

use Illuminate\Database\Eloquent\Casts\Attribute;
use App\Services\LocationService;
use App\Models\Location;

//
    public function distanceToOffice(): Attribute
    {
        $office = Location::office()->first();

        $distanceFn = fn() => LocationService::distance(
            origin: $this->navigationAddress,
            destination: $office->navigationAddress
        );

        return Attribute::make(get: $distanceFn)
            ->shouldCache()
        ;
    }

still using the same blade template, suddenly the db query generated by Location::office()->first() is invoked hundreds of times; around 325 times, in fact. And I don't understand why or how.

EDIT

And a few minutes later, I modified the latter accessor as follows:

    public function distanceToOffice(): Attribute
    {
        # resolve the model later
        $resolveOffice = fn() => Location::office()->first();

        $distanceFn = fn() => LocationService::distance(
            origin: $this->navigationAddress,
            destination: $resolveOffice()->navigationAddress
        );

        return Attribute::make(get: $distanceFn)
            ->shouldCache()
        ;
    }

Such that the office location model is resolved/loaded when that Attribute's getter function is actually called (I guess?).. and now the duplicate query is back down from 320-something, to just 2x.

I still would like to understand the how the upwards of 300 invocations is occurring; one User (or Teacher) model and one Location model involved here.

0 likes
0 replies

Please or to participate in this conversation.