filamentphp / filament

A collection of beautiful full-stack components for Laravel. The perfect starting point for your next app. Using Livewire, Alpine.js and Tailwind CSS.
https://filamentphp.com
MIT License
17.05k stars 2.69k forks source link

MorphOneOfMany Relationship Building Query Improperly with Select #13555

Open blhylton opened 1 month ago

blhylton commented 1 month ago

Package

filament/filament

Package Version

v3.2.92

Laravel Version

v11.15.0

Livewire Version

No response

PHP Version

PHP 8.2.21 | PHP 8.3.9

Problem description

Models with MorphOneOfMany (and probably all OneOfMany) relationships do not build the query correctly and cause an error when loading the forms. Essentially, what appears to be happening is that Filament sees the MorphOne relation returned and doesn't account for their being a pivot table, looking for the morph_id on the related models table instead.

I've given as much detail as is feasible below, as well as a reproduction repo. Instructions on how to use the repo to reproduce are included below, as well as in the repo's Readme.

Expected behavior

For the query to be a working query, and for the select box to populate options that are relevant to the morphOne without using modifyQueryUsing.

Steps to reproduce

General Instructions

  1. Set up a morph one to many relationship on a model that has a Filament resource/form.
  2. Create a select field attached to relationship on resource/form.
  3. Navigate to resource and attempt to create or edit.

Using the repo linked below

Note: I moved the panel to the root directory and removed the auth gate to simplify access to the resources for testing.

  1. Clone repo
  2. composer install
  3. php artisan migrate --seed
  4. Navigate to rooms resource in filament, and attempt to create or edit. Rooms resource is incredibly barebones for the sake of simplified testing.

Don't see a better place to note this: My logs below are from the linked test repository, using SQLite, but I was originally seeing this with MySQL, confirming that it not RDBMS platform dependent.

Reproduction repository (issue will be closed if this is not valid)

https://github.com/blhylton/filament-morph-otm-repro

Relevant log output

[previous exception] [object] (PDOException(code: HY000): SQLSTATE[HY000]: General error: 1 no such column: products.productable_id at [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Connection.php:407)
[stacktrace]
#0 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Connection.php(407): PDO->prepare('select \"product...')
#1 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Connection.php(800): Illuminate\\Database\\Connection->Illuminate\\Database\\{closure}('select \"product...', Array)
#2 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Connection.php(767): Illuminate\\Database\\Connection->runQueryCallback('select \"product...', Array, Object(Closure))
#3 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Connection.php(398): Illuminate\\Database\\Connection->run('select \"product...', Array, Object(Closure))
#4 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2996): Illuminate\\Database\\Connection->select('select \"product...', Array, true)
#5 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2981): Illuminate\\Database\\Query\\Builder->runSelect()
#6 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(3569): Illuminate\\Database\\Query\\Builder->Illuminate\\Database\\Query\\{closure}()
#7 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2980): Illuminate\\Database\\Query\\Builder->onceWithColumns(Array, Object(Closure))
#8 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(759): Illuminate\\Database\\Query\\Builder->get(Array)
#9 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(741): Illuminate\\Database\\Eloquent\\Builder->getModels(Array)
#10 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Concerns/BuildsQueries.php(343): Illuminate\\Database\\Eloquent\\Builder->get(Array)
#11 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/MorphOne.php(31): Illuminate\\Database\\Eloquent\\Builder->first()
#12 [PROJECT DIRECTORY]/vendor/filament/forms/src/Components/Select.php(852): Illuminate\\Database\\Eloquent\\Relations\\MorphOne->getResults()
#13 [PROJECT DIRECTORY]/vendor/filament/support/src/Concerns/EvaluatesClosures.php(35): Filament\\Forms\\Components\\Select::Filament\\Forms\\Components\\{closure}(Object(Filament\\Forms\\Components\\Select), NULL)
#14 [PROJECT DIRECTORY]/vendor/filament/forms/src/Components/Concerns/BelongsToModel.php(87): Filament\\Support\\Components\\Component->evaluate(Object(Closure))
#15 [PROJECT DIRECTORY]/vendor/filament/forms/src/Components/Concerns/HasState.php(240): Filament\\Forms\\Components\\Component->loadStateFromRelationships()
#16 [PROJECT DIRECTORY]/vendor/filament/forms/src/Components/Select.php(1222): Filament\\Forms\\Components\\Component->hydrateDefaultState(NULL)
#17 [PROJECT DIRECTORY]/vendor/filament/forms/src/Components/Concerns/HasState.php(216): Filament\\Forms\\Components\\Select->hydrateDefaultState(NULL)
#18 [PROJECT DIRECTORY]/vendor/filament/forms/src/Concerns/HasState.php(203): Filament\\Forms\\Components\\Component->hydrateState(NULL, true)
#19 [PROJECT DIRECTORY]/vendor/filament/forms/src/Concerns/HasState.php(188): Filament\\Forms\\ComponentContainer->hydrateState(NULL, true)
#20 [PROJECT DIRECTORY]/vendor/filament/filament/src/Resources/Pages/EditRecord.php(109): Filament\\Forms\\ComponentContainer->fill(Array)
#21 [PROJECT DIRECTORY]/vendor/filament/filament/src/Resources/Pages/EditRecord.php(92): Filament\\Resources\\Pages\\EditRecord->fillFormWithDataAndCallHooks(Object(App\\Models\\Room))
#22 [PROJECT DIRECTORY]/vendor/filament/filament/src/Resources/Pages/EditRecord.php(79): Filament\\Resources\\Pages\\EditRecord->fillForm()
#23 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Filament\\Resources\\Pages\\EditRecord->mount('1')
#24 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Container/Util.php(41): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#25 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\\Container\\Util::unwrapIfClosure(Object(Closure))
#26 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(35): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#27 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Wrapped.php(23): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array)
#28 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Features/SupportLifecycleHooks/SupportLifecycleHooks.php(134): Livewire\\Wrapped->__call('mount', Array)
#29 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Features/SupportLifecycleHooks/SupportLifecycleHooks.php(20): Livewire\\Features\\SupportLifecycleHooks\\SupportLifecycleHooks->callHook('mount', Array)
#30 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/ComponentHook.php(19): Livewire\\Features\\SupportLifecycleHooks\\SupportLifecycleHooks->mount(Array, false)
#31 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/ComponentHookRegistry.php(45): Livewire\\ComponentHook->callMount(Array, false)
#32 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/EventBus.php(60): Livewire\\ComponentHookRegistry::Livewire\\{closure}(Object(App\\Filament\\Resources\\RoomResource\\Pages\\EditRoom), Array, NULL, false)
#33 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/helpers.php(98): Livewire\\EventBus->trigger('mount', Object(App\\Filament\\Resources\\RoomResource\\Pages\\EditRoom), Array, NULL, false)
#34 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php(50): Livewire\\trigger('mount', Object(App\\Filament\\Resources\\RoomResource\\Pages\\EditRoom), Array, NULL, false)
#35 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/LivewireManager.php(73): Livewire\\Mechanisms\\HandleComponents\\HandleComponents->mount('App\\\\Filament\\\\Re...', Array, NULL)
#36 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Features/SupportPageComponents/HandlesPageComponents.php(17): Livewire\\LivewireManager->mount('App\\\\Filament\\\\Re...', Array)
#37 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Features/SupportPageComponents/SupportPageComponents.php(117): Livewire\\Component->Livewire\\Features\\SupportPageComponents\\{closure}()
#38 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Features/SupportPageComponents/HandlesPageComponents.php(14): Livewire\\Features\\SupportPageComponents\\SupportPageComponents::interceptTheRenderOfTheComponentAndRetreiveTheLayoutConfiguration(Object(Closure))
#39 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(46): Livewire\\Component->__invoke('1')
#40 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(App\\Filament\\Resources\\RoomResource\\Pages\\EditRoom), '__invoke')
#41 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()
#42 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Router.php(808): Illuminate\\Routing\\Route->run()
#43 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#44 [PROJECT DIRECTORY]/vendor/filament/filament/src/Http/Middleware/DispatchServingFilamentEvent.php(15): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#45 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Filament\\Http\\Middleware\\DispatchServingFilamentEvent->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#46 [PROJECT DIRECTORY]/vendor/filament/filament/src/Http/Middleware/DisableBladeIconComponents.php(14): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#47 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Filament\\Http\\Middleware\\DisableBladeIconComponents->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#48 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#49 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#50 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#51 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#52 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Session/Middleware/AuthenticateSession.php(48): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#53 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\AuthenticateSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#54 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#55 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#56 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#57 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#58 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#59 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#60 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#61 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(75): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#62 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#63 [PROJECT DIRECTORY]/vendor/filament/filament/src/Http/Middleware/SetUpPanel.php(19): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#64 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Filament\\Http\\Middleware\\SetUpPanel->handle(Object(Illuminate\\Http\\Request), Object(Closure), Object(Filament\\Panel))
#65 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#66 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Router.php(807): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#67 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Router.php(786): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#68 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Router.php(750): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#69 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Routing/Router.php(739): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#70 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(200): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#71 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#72 [PROJECT DIRECTORY]/vendor/livewire/livewire/src/Features/SupportDisablingBackButtonCache/DisableBackButtonCacheMiddleware.php(19): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#73 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Livewire\\Features\\SupportDisablingBackButtonCache\\DisableBackButtonCacheMiddleware->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#74 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#75 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#76 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#77 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#78 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php(51): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#79 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#80 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#81 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#82 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(110): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#83 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#84 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#85 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#86 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#87 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#88 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#89 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#90 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#91 [PROJECT DIRECTORY]/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(1188): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#92 [PROJECT DIRECTORY]/public/index.php(17): Illuminate\\Foundation\\Application->handleRequest(Object(Illuminate\\Http\\Request))
#93 /Applications/Herd.app/Contents/Resources/valet/server.php(155): require('[REDACTED FOR PRIVACY]')
#94 {main}
"}

Donate 💰 to fund this issue

Fund with Polar

github-actions[bot] commented 1 month ago

Hey @blhylton! We're sorry to hear that you've hit this issue. 💛

However, it doesn't look like you've provided much information on how to replicate the issue. Please edit your original post with clear steps we need to take.

blhylton commented 1 month ago

In case anyone comes across this before there's a proper upstream fix, there is a workaround utilizing the form events.

Your Eloquent model with the MorphOneOfMany will need a traditional MorphMany on it for the relationship.

...
// You need this in addition to the MorphOneOfMany relations
public function products(): MorphToMany
{
    return $this->morphToMany(Product::class, 'productable');
}

public function oneWeekProduct()
{
    return $this->morphToMany(Product::class, 'productable')
        ->where('duration', 7)
        ->latestOfMany();
}
...

Your resource will then use the MorphMany as it's relationship.

...
Select::make('oneWeekProduct')
    ->rules('exists:products,id')
    ->label('One Week Product')
    ->createOptionForm(ProductResource::getSchemaFields())
    ->relationship(
        name: 'products',
        titleAttribute: 'name',
        modifyQueryUsing: fn (Builder $query) => $query->where('duration', 7)),
...

Then, you'll utilize Filament's Lifecycle hooks and mutateFormBeforeSave/mutateFormBeforeCreate to manipulate the data the way you need.

...
protected function mutateFormDataBeforeCreate(array $data): array
{
    // I have more than one here, just showing you one for simplicity's sake
    $this->product_ids = collect($data)->only([
        'oneWeekProduct',
    ]);

    unset(
        $data['oneWeekProduct']
    );

    return parent::mutateFormDataBeforeCreate($data);
}

protected function afterCreate(): void
{
    $this->record->products()->sync($this->product_ids);
}

Major caveat to this, if you edit the resource without changing anything, it will return an array instead of a single value, so you'll need to account for that as well:

...
protected function mutateFormDataBeforeSave(array $data): array
{
    $this->product_ids = collect($data)->only([
        'oneWeekProduct',
    ]);

    $this->product_ids = $this->product_ids
        ->mapWithKeys(
            fn ($value, $key) => is_iterable($value)
                ? [$key => collect($value)->first()]
                : [$key => $value]
        );

    unset(
        $data['oneWeekProduct']
    );

    return parent::mutateFormDataBeforeSave($data);
}
...

Hopefully this helps if someone else finds theirself having to work around this for now. I caught it because I was code reviewing for one of my teammates and couldn't figure out why he was adding extra stuff to get this to work.