F
Filament12mo ago
Shavik

Nullable PHP Enum in Table TextColumn Oddity

I have a model/table that is a collection of various statuses (PHP Enum values) that are nullable if it's never been set. When using a TextColumn like so:
Tables\Columns\TextColumn::make('state.current_confirmation_status')
->label('Confirmation')
->placeholder('Unknown')
->badge()
->sortable(),
Tables\Columns\TextColumn::make('state.current_confirmation_status')
->label('Confirmation')
->placeholder('Unknown')
->badge()
->sortable(),
If the field actually has an enum value, it works and shows the badge as expected. If the value in the column is null, then the placeholder (nor default) doesn't properly set a 'fallback' state. What am I missing?
No description
21 Replies
awcodes
awcodes12mo ago
Maybe getStateUsing to return a default if null?
Shavik
ShavikOP12mo ago
Hmm that does set it. Would that break the 'magical' HasLabel / HasColor interfaces for enums?
Shavik
ShavikOP12mo ago
->getStateUsing(fn ($record) => $record->state->current_confirmation_status ?? 'Unknown')
->getStateUsing(fn ($record) => $record->state->current_confirmation_status ?? 'Unknown')
No description
Shavik
ShavikOP12mo ago
I guess I should make an 'unknown' state? Hmm surprised to see that 'null' values don't trigger the default / placeholder 'handlers' I guess that's due to the Enum cast on the model's attribute?
awcodes
awcodes12mo ago
Could be Not sure
Shavik
ShavikOP12mo ago
Ok, well still appreciate the help! I suppose I can make an 'unknown' enum state but not ever 'store' that in the db so the null db state would trigger the frontend's 'unknown' handler state. Hmm guess that means I'd have to do that 'handling' everywhere Been thinking about this and I haven't looked at the relevant filament source code yet but I think I understand why this is 'diifficult to handle' in terms of how could you '->getLabel/Color' on a null enum But perhaps I could do a PR that would add ->defaultLabel() and ->defaultColor for the TextColumn when it's handling an enum that is null Or maybe a defaultLabel / defaultColor interface you could add to your enum that has static function signatures for getDefaultLabel and getDefaultColor respectively Not sure which would be better
DarkKnight
DarkKnight12mo ago
There is a cool trick you can do. You can cast the column to an Enum via the model
Shavik
ShavikOP12mo ago
I do that
DarkKnight
DarkKnight12mo ago
And I think everything else is handled automatically by filament
Shavik
ShavikOP12mo ago
That's how it takes advantage of the getLabel and getColor The problem is that it's a nullable field like if we have never received a status update for that field from the external system, the column/attribute/enum value is null. So it leaves the column / badge blank I was expecting the placeholder / default method on the textcolumn to allow me to set what to show when the attribute is null
DarkKnight
DarkKnight12mo ago
Isnt there a defaut case in the Enum? I think so
Shavik
ShavikOP12mo ago
🙂 that was my first thought but that is nonsensical since you can't call ->getLabel on a null value
DarkKnight
DarkKnight12mo ago
But you call getLabel based on the case, no? if you have a case
enum Status: string implements HasLabel
{
case Draft = 'draft';
case Reviewing = 'reviewing';
case Published = 'published';
case Rejected = 'rejected';
case NullValue = null;

public function getLabel(): ?string
{

return match ($this) {
self::Draft => 'Draft',
self::Reviewing => 'Reviewing',
self::Published => 'Published',
self::Rejected => 'Rejected',
self::NullValue => 'whatever'
};
}

public function getColor(): string | array | null
{
return match ($this) {
self::Draft => 'gray',
self::Reviewing => 'warning',
self::Published => 'success',
self::Rejected => 'danger',
self::NullValue => 'danger'
};
}
}
enum Status: string implements HasLabel
{
case Draft = 'draft';
case Reviewing = 'reviewing';
case Published = 'published';
case Rejected = 'rejected';
case NullValue = null;

public function getLabel(): ?string
{

return match ($this) {
self::Draft => 'Draft',
self::Reviewing => 'Reviewing',
self::Published => 'Published',
self::Rejected => 'Rejected',
self::NullValue => 'whatever'
};
}

public function getColor(): string | array | null
{
return match ($this) {
self::Draft => 'gray',
self::Reviewing => 'warning',
self::Published => 'success',
self::Rejected => 'danger',
self::NullValue => 'danger'
};
}
}
Not sure if i understand what you want :/
Shavik
ShavikOP12mo ago
Ah I hadn't considered actually having a case where the value is null
DarkKnight
DarkKnight12mo ago
Try this, and let me know
Shavik
ShavikOP12mo ago
But I think that would cause a PHP error since the backing type of the enum is "string" instead of "?string" or "string | null" I believe the only valid backing types are int and string
DarkKnight
DarkKnight12mo ago
yes, pls change that Yeah null is not considered a valid scalar Ok I have another idea Another method is to create a specific case to represent the "null" or "undefined" state, and use a different backing value (like an empty string or a specific keyword) for it.
enum Status: string implements HasLabel
{
case Draft = 'draft';
case Reviewing = 'reviewing';
case Published = 'published';
case Rejected = 'rejected';
case None = 'none'; // Representing the 'null' state with a specific value

public function getLabel(): ?string
{
return match ($this) {
self::Draft => 'Draft',
self::Reviewing => 'Reviewing',
self::Published => 'Published',
self::Rejected => 'Rejected',
self::None => 'None'
};
}

public function getColor(): string | array | null
{
return match ($this) {
self::Draft => 'gray',
self::Reviewing => 'warning',
self::Published => 'success',
self::Rejected => 'danger',
self::None => null
};
}
}
enum Status: string implements HasLabel
{
case Draft = 'draft';
case Reviewing = 'reviewing';
case Published = 'published';
case Rejected = 'rejected';
case None = 'none'; // Representing the 'null' state with a specific value

public function getLabel(): ?string
{
return match ($this) {
self::Draft => 'Draft',
self::Reviewing => 'Reviewing',
self::Published => 'Published',
self::Rejected => 'Rejected',
self::None => 'None'
};
}

public function getColor(): string | array | null
{
return match ($this) {
self::Draft => 'gray',
self::Reviewing => 'warning',
self::Published => 'success',
self::Rejected => 'danger',
self::None => null
};
}
}
Shavik
ShavikOP12mo ago
That was one option I had considered but due to some reasons on the backend, I probably need to stick with the nullable value
DarkKnight
DarkKnight12mo ago
Can you try this?
awcodes
awcodes12mo ago
Wonder if you could just use default on the match statements. Still thinks it’s a caveat in the table though where null values just aren’t rendered.
DrByte
DrByte12mo ago
I've seen a few other reports of this same issue, here in Discord. Seems reasonable to me to expect that a null should allow placeholder() and default() to operate. Maybe we just need the component to check for being cast to enum? or maybe the current checking is doing a specific override when the enum is null, and we need to stop that override?
Want results from more Discord servers?
Add your server