etfz's avatar
Level 1

Eager loading accessor?

Hi,

I have an accessor like this

	public function getUnknownItemsAttribute() {
		return Item
			::where('source_id', $this->id)
			->whereNotNull('dependency_filename')
			->doesntHave('models')
			->doesntHave('items')
			->distinct('dependency_filename')
			->orderBy('dependency_filename')
			->get();
	}

which I use like so

					@if ($source->unknownItems->isNotEmpty())
						<div>
							<ul>
								@foreach ($source->unknownItems as $item)
									<li><span class="text-danger">{{ $item->dependency_filename }}</span></li>
								@endforeach
							</ul>
						</div>
					@endif

which causes the the query to be performed twice. I can work around it by assigning it to a variable in the controller or similar.

	public function show(Source $source) {
		$source->unknownItemsLoaded = $source->unknownItems;

		return view('source.show', [ 'source' => $source ]);
	}

Is that how I need to do this? Is there no proper way to do it?

0 likes
5 replies
ramonrietdijk's avatar
Level 30

You could make it an attribute. It seems that objects are cached by default.

This is possible because Eloquent retains instances returned by accessors so it can return the same instance each time the accessor is invoked

I believe the following should work:

use Illuminate\Database\Eloquent\Casts\Attribute;

protected function unknownItems(): Attribute
{
    return Attribute::make(
        get: fn () => Item::where('source_id', $this->id)
			->whereNotNull('dependency_filename')
			->doesntHave('models')
			->doesntHave('items')
			->distinct('dependency_filename')
			->orderBy('dependency_filename')
			->get()
    );
}
1 like
etfz's avatar
Level 1

@ramonrietdijk Ah! Seems to work. Didn't realise the new attribute notation had such a feature. Thank you!

1 like
Snapey's avatar

Bear in mind that you CANNOT eager load dynamic attributes.

Please or to participate in this conversation.