Open blhylton opened 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.
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.
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 allOneOfMany
) 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 theMorphOne
relation returned and doesn't account for their being a pivot table, looking for themorph_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
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.
composer install
php artisan migrate --seed
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
Donate 💰 to fund this issue