Eager-loaded relations throw "Attempted to lazy load" error when using Action on View page
https://github.com/user-attachments/assets/83a89c4b-6243-4fec-9e28-1777cd74b235
Package
filament/filament
Package Version
v3.3.14
Laravel Version
v12.13.0
Livewire Version
No response
PHP Version
PHP 8.4
Problem description
I'm working on an OrderResource where the Order model has two hasMany relations: items and totals. Lazy loading is disabled via Model::preventLazyLoading() in my application.
The Order model has a currency_code column. We need this column value when formatting values on the Items and totals models.
In the View page of the resource, I correctly eager-load both relationships using load() inside the mount() method like this:
public function mount(int|string $record): void
{
parent::mount($record);
$this->record->load([
'items' => fn ($query) => $query->chaperone(),
'totals' => fn ($query) => $query->chaperone(),
]);
}
This works fine when the page loads.
However, when I add a custom button to update the order status (a Filament\Actions\Action), clicking on it results in this error:
Attempted to lazy load [order] on model [App\Models\Business\Order\OrderTotal].
https://drive.google.com/file/d/1WXs_oG1vStIEx5eWZwTDrBv8gcdVL1Ez/view
Expected behavior
The eager-loaded relations should persist, or actions should not try to access relations that weren’t explicitly loaded or provide a way to load the missing relationship.
Steps to reproduce
- composer install
- npm install
- npm run build
- php artisan migrate:fresh --seed
- login user: [email protected], password: password
- Go to the Orders resource.
- Click View on any order.
- Click the Update Status button.
- The issue will appear.
Reproduction repository (issue will be closed if this is not valid)
https://github.com/devsumitparkash/filmaent-lazy-load-disabled-issue
Relevant log output
Same thing happening here which makes it difficult to create complex logic without resorting to lazy loading.
I did not eager-load the relationships in the View but I did it in the getEloquentQuery method Resource.php:
public static function getEloquentQuery(): \Illuminate\Database\Eloquent\Builder
{
return parent::getEloquentQuery()
->withoutGlobalScopes()
->with('a')
->with('b');
}
Hi @devsumitparkash
This works:
Actions\Action::make('update-order-status')
->mountUsing(function (Action $action) {
$this->record->load([
'items' => fn ($query) => $query->chaperone(),
'totals' => fn ($query) => $query->chaperone(),
]);
})
Your OrderTotal model has computed attributes that depend on the parent Order model:
protected function valueAsMoneyObject(): Attribute
{
$column = 'value';
return Attribute::make(
get: function (mixed $value, array $attributes) use ($column): Money {
return $this->monetaryAsMoneyObject($column, $this->order->currency_code);
},
);
}
Which triggers the lazy loading error when the modal opens because Livewire doesn't persist eager-loaded relationships between requests. The page's mount() method runs on page load, but clicking the action is a new request. Using mountUsing() on the action makes sure relationships are loaded each time the action is mounted.
@danharrin This can be closed, unless you think Filament should handle this automatically somehow? See: https://github.com/devsumitparkash/filmaent-lazy-load-disabled-issue/blob/3b8ab0d360295f02f152004a9f31686b051ac5fb/app/Filament/Resources/OrderResource/Pages/ViewOrder.php#L36
You're right @binaryfire, mount() is only executed on the first Livewire request too, which is not the same request as when the modal is opened, which is why the eager loading is not persisted
This works:
Actions\Action::make('update-order-status') ->mountUsing(function (Action $action) { $this->record->load([ 'items' => fn ($query) => $query->chaperone(), 'totals' => fn ($query) => $query->chaperone(), ]); }) Your
OrderTotalmodel has computed attributes that depend on the parentOrdermodel:protected function valueAsMoneyObject(): Attribute { $column = 'value';
return Attribute::make( get: function (mixed $value, array $attributes) use ($column): Money { return $this->monetaryAsMoneyObject($column, $this->order->currency_code); }, );} Which triggers the lazy loading error when the modal opens because Livewire doesn't persist eager-loaded relationships between requests. The page's
mount()method runs on page load, but clicking the action is a new request. UsingmountUsing()on the action makes sure relationships are loaded each time the action is mounted.@danharrin This can be closed, unless you think Filament should handle this automatically somehow? See: https://github.com/devsumitparkash/filmaent-lazy-load-disabled-issue/blob/3b8ab0d360295f02f152004a9f31686b051ac5fb/app/Filament/Resources/OrderResource/Pages/ViewOrder.php#L36
Thanks for the suggestion — adding the ->mountUsing(...) method does help with eager loading and avoids the lazy loading error when using chaperone(). However, I’m noticing a new issue where the default value of a Select field isn’t getting selected anymore.
Here’s the field definition:
Select::make('status')
->options(
collect(OrderStatus::cases())->mapWithKeys(fn ($case) => [
$case->value => $case->getLabel(),
])->toArray(),
)
->default(fn (Order $record) => $record->status->value)
->required(),
When I use ->mountUsing(...) and manually load the relationships, the default is not selected. But if I remove mountUsing() and instead just apply ->chaperone() on the relationship like:
public function items(): HasMany
{
return $this->hasMany(OrderItem::class)->chaperone();
}
...then the default value works as expected.
Also worth noting: if I remove the ->successRedirectUrl(...) and just show a success notification, I get the following error:
Attempted to lazy load [order] on model [App\Models\Business\Order\OrderItem] but lazy loading is disabled.
Any ideas, please?
Also, make sure to pull the latest changes (git pull) to get the updated implementation.