Enum cast not working when editing a resource

I have a model "Article" with a "category" field. This Field is being cast to an enum ArticleCategory::class. The Enum class is implementing Filament\Support\Contracts\HasLabel. In my ArticleResource form I am populating a select input using ->options(ArticleCategory::class). I am also providing ->default(ArticleCategory::STORY) to set the default during article creation. Further down in my form I have a conditional grid, which only gets displayed, if $get('category') matches ArticleCategory::REVIEW Everything works when creating a new article. During editing however, I noticed that my conditional grid is never displayed. Upon further inspection I found out that the value for "category" was not an instance of my Enum class, but just a plain String. I was able to fix my issue using this piece of Code in my EditArticle.php file:
protected function mutateFormDataBeforeFill(array $data): array
{
$data['category'] = ArticleCategory::from($data['category']);

return $data;
}
protected function mutateFormDataBeforeFill(array $data): array
{
$data['category'] = ArticleCategory::from($data['category']);

return $data;
}
This feels rather cumbersome, as I was expecting that Filament would take my Enum Cast into account during editing, considering that the Select Input works so nicely with Enum classes. Is this a bug? Or is there a simpler way to do this? Or is this already the proper way to do this?
24 Replies
DrByte
DrByte7mo ago
can you post the code for your Form? I'm wondering how you're presenting the conditional, in the context of the rest of how the form is constructed.
DrByte
DrByte7mo ago
okay. I'm trying to narrow things down to where the code could check for is_a(enum) and have it call ->value on it. Just gotta figure out what's needing the code added into to support it. ... cuz if it's a reasonable expectation to need to support it, then a PR is probably easily welcomed.
awcodes
awcodes7mo ago
Casts only work when data goes into and out of the database. So when referencing $data the cast is not relevant so anything you do with data would default to how enums are used natively in php. So, in $data it won’t be an enum it will be a string.
Samus_Aran
Samus_Aran7mo ago
There might be something else going on. As I continue working on my Form I now get a Livewire crash with "Object of class App\Enums\ArticleCategory could not be converted to string" when submitting my form An additional livewire error get's thrown when I change the value of my select input: Uncaught (in promise) TypeError: Cannot convert undefined or null to object at Function.values (<anonymous>) at file-upload.js?v=3.1.11.0:40:84644 at livewire.js?id=f477dd12:844:11 Not 100% sure where the issue originates as I basically copypasted the code from the doc section mentioned above
DrByte
DrByte7mo ago
Is all this related to your "conditional" segment? Does the problem follow if you remove that conditional segment?
Samus_Aran
Samus_Aran7mo ago
yeah, seems to be related to that
DrByte
DrByte7mo ago
If you add things back bit by bit, which part "breaks" it?
Samus_Aran
Samus_Aran7mo ago
can't really narrow it down further. For a moment I thought it could be an issue that my conditional grid includes other sections in its schema, but the bug only disappears if i remove the full conditional grid
DrByte
DrByte7mo ago
Mind posting your code for the class? I know you said it's almost straight from the docs, but you mentioned adding the mutate, and cuztomizing the grid, etc.
awcodes
awcodes7mo ago
Yea, code would help us. Mutate happens at a different lifecycle point than rendering does.
Samus_Aran
Samus_Aran7mo ago
Gist
ArticleResource
ArticleResource. GitHub Gist: instantly share code, notes, and snippets.
DrByte
DrByte7mo ago
Does this error disappear if you remove the file-upload component? I don't see any reason for that to be a problem in your code. Unless it's related to something the SpatieFileUpload component is drawing from your Model.
I suppose you could try switching it to a regular FileUpload component as an alternate test (but I understand how that's not desirable when you've got the other already integrated). If the error persists (ie: falls through to another element) that'll reveal more. Looks like you removed the mutateFormDataBeforeFill() call?
awcodes
awcodes7mo ago
For starters this is going to break anything and everything ->afterStateUpdated(fn (Forms\Components\Select $component) => $component
awcodes
awcodes7mo ago
Sorry, formatting issue. I’m on my phone.
Samus_Aran
Samus_Aran7mo ago
Yeah figured that also out just now 😄 its 3AM, I'll probably need to continue tomorrow with a fresh pair of eyes. But thank you guys so much already for helping me figuring this out!
awcodes
awcodes7mo ago
It could be the fact that you are using dot syntax on the grid’s sub fields. If that is not a relationship you should probably define a statePath on the grid and not use dot syntax for the field’s make() names. I don’t think dot syntax works on form fields the way it does on tables.
Samus_Aran
Samus_Aran7mo ago
Ill check it tomorrow. But the form fills perfectly when opening an existing article from my DB so i just assumed its all Fine Like this 🤔
Samus_Aran
Samus_Aran7mo ago
@awcodes @DrByte Okay, so i figured out a few things: 1. The code for afterStateUpdated() at https://filamentphp.com/docs/3.x/forms/advanced#dynamic-fields-based-on-a-select-option did not work for me, because my conditional grid had other layout components nested in it's schema. This caused the fill() method to mess up my form. I had to adjust the code like this to get it working:
->afterStateUpdated(function (Forms\Components\Select $component, Livewire $livewire, $state) {
if($state === ArticleCategory::REVIEW){
$reviewDetailsWrapper = $component
->getContainer()
->getComponent('reviewDetailsWrapper')
->getChildComponentContainer();

$reviewDetailsWrapper
->getComponent('reviewRating')
->getChildComponentContainer()
->fill($livewire->data);
$reviewDetailsWrapper
->getComponent('reviewDetails')
->getChildComponentContainer()
->fill($livewire->data);
}
}),
->afterStateUpdated(function (Forms\Components\Select $component, Livewire $livewire, $state) {
if($state === ArticleCategory::REVIEW){
$reviewDetailsWrapper = $component
->getContainer()
->getComponent('reviewDetailsWrapper')
->getChildComponentContainer();

$reviewDetailsWrapper
->getComponent('reviewRating')
->getChildComponentContainer()
->fill($livewire->data);
$reviewDetailsWrapper
->getComponent('reviewDetails')
->getChildComponentContainer()
->fill($livewire->data);
}
}),
2. Fixing the first part introduced another issue. When editing an existing "review" article, all field in my conditional grid would loose their values forever, if I would switch the article category from "review' to something else and then back to "review". I was able to fix this by injecting the Livewire component instance and passing it's data to the fill() method.
Samus_Aran
Samus_Aran7mo ago
3. There seems to be a bug with the DatePicker and DateTimePicker when using their JavaScript implementation with ->native(false). As soon as I change my article category using the select modal my console is getting flodded with stuff like "Alpine Expression Error: focusedMonth is not define" followed by errors like this:
Uncaught ReferenceError: focusedMonth is not defined
at [Alpine] focusedMonth (eval at safeAsyncFunction (livewire.js?id=f477dd12:1319:21), <anonymous>:3:32)
at livewire.js?id=f477dd12:1341:23
at tryCatch (livewire.js?id=f477dd12:1261:14)
at getValue (livewire.js?id=f477dd12:3328:7)
at livewire.js?id=f477dd12:3385:19
at reactiveEffect (livewire.js?id=f477dd12:2435:18)
at Object.effect2 [as effect] (livewire.js?id=f477dd12:2410:7)
at effect (livewire.js?id=f477dd12:800:35)
at livewire.js?id=f477dd12:1967:26
at wrappedEffect (livewire.js?id=f477dd12:816:29)
Uncaught ReferenceError: focusedMonth is not defined
at [Alpine] focusedMonth (eval at safeAsyncFunction (livewire.js?id=f477dd12:1319:21), <anonymous>:3:32)
at livewire.js?id=f477dd12:1341:23
at tryCatch (livewire.js?id=f477dd12:1261:14)
at getValue (livewire.js?id=f477dd12:3328:7)
at livewire.js?id=f477dd12:3385:19
at reactiveEffect (livewire.js?id=f477dd12:2435:18)
at Object.effect2 [as effect] (livewire.js?id=f477dd12:2410:7)
at effect (livewire.js?id=f477dd12:800:35)
at livewire.js?id=f477dd12:1967:26
at wrappedEffect (livewire.js?id=f477dd12:816:29)
Aaaand I think that's beyond my knowledge. If I use the native DatePicker the error goes away. That being said, my form now seems to work perfectly, both for creating and editing. My issue however persists: when submitting my form (both on edit or create) I get "Object of class App\Enums\ArticleCategory could not be converted to string". But I was able to narrow this down a bit: - The issue only occurs when my article category is set to 'review' - The issue disappears when i remove my conditional grid. Hope this info helps!
awcodes
awcodes7mo ago
Can you share the actual Enum class?
Samus_Aran
Samus_Aran7mo ago
Wait, I think I just found the error. So I removed the cast from my model and... the issue persisted. Which was really weird. Then I saw that at the end of my form, in my conditional grid. I had two fields with this piece of code: ->requiredIf('category', ArticleCategory::REVIEW) That was the issue. Changing it to ->requiredIf('category', ArticleCategory::REVIEW->value) fixed the bug. However, as both fiels are inside the conditional grid, I didn't even need the check and could just use ->required()
DrByte
DrByte7mo ago
Glad you got it sorted!