F
Filament11mo ago
Tieme

Enum select with live() on Create and Edit form

Hi All, I have a question, i use a select with Enum and some dependecie fields/fieldset on this value.
Forms\Components\Select::make('row_type')
->label(Helpers::translate('Row Type'))
->options(RowType::class)
->default(RowType::NORMAL)
->live()
->required()
->native(false),
Forms\Components\Select::make('row_type')
->label(Helpers::translate('Row Type'))
->options(RowType::class)
->default(RowType::NORMAL)
->live()
->required()
->native(false),
The fieldset
Forms\Components\Fieldset::make(Helpers::translate('Product settings'))
->schema([])
->columns(12)
->visible(fn (Forms\Get $get): bool => $get('row_type') == RowType::NORMAL)
Forms\Components\Fieldset::make(Helpers::translate('Product settings'))
->schema([])
->columns(12)
->visible(fn (Forms\Get $get): bool => $get('row_type') == RowType::NORMAL)
On create record this works, on edit record this works not. That is because on create $get('row_type') is Enum and on Edit it is integer even tho i cast it in the model
protected $casts = [
'row_type' => RowType::class,
];
protected $casts = [
'row_type' => RowType::class,
];
For now i have create a helper to get this to work.
public static function GetValueFromEnum(mixed $enum)
{
if (is_numeric($enum)) {
return $enum;
}

return $enum->value;
}
public static function GetValueFromEnum(mixed $enum)
{
if (is_numeric($enum)) {
return $enum;
}

return $enum->value;
}
This is how i use it now
->visible(fn (Forms\Get $get): bool => Helpers::GetValueFromEnum($get('row_type')) == RowType::NORMAL->value)
->visible(fn (Forms\Get $get): bool => Helpers::GetValueFromEnum($get('row_type')) == RowType::NORMAL->value)
It works, but i think there is a better and simpler solution only dont know how. Can anyone point me in the right direction? Thanks
4 Replies
Tieme
TiemeOP11mo ago
up
Benjamin
Benjamin4mo ago
Same problem here ! Up 🤚
sadiqgoni13
sadiqgoni134mo ago
The issue you’re facing is that when editing a record, the value retrieved from the $get('row_type') is an integer, whereas during the creation process, it's an Enum instance. Instead of modifying the visible condition, you can modify the field setup to always return an Enum instance, whether you're creating or editing. This can be done by transforming the value using the afterStateHydrated method.
Forms\Components\Select::make('row_type')
->label(Helpers::translate('Row Type'))
->options(RowType::class)
->default(RowType::NORMAL)
->live()
->required()
->native(false)
->afterStateHydrated(function ($component, $state) {
if (is_numeric($state)) {
$component->state(RowType::from($state));
}
}),
Forms\Components\Select::make('row_type')
->label(Helpers::translate('Row Type'))
->options(RowType::class)
->default(RowType::NORMAL)
->live()
->required()
->native(false)
->afterStateHydrated(function ($component, $state) {
if (is_numeric($state)) {
$component->state(RowType::from($state));
}
}),
Benjamin
Benjamin4mo ago
Great solution @sadiqgoni13 ! On my side, I only use string back enums, so I made something similar but using a FilamentService with isEqualEnum() method :
/**
* Useful for Filament forms that sometimes parse enums as strings and sometimes as objects.
*
* @param mixed $value
* @param string|list<string> $toBeEqual
* @return boolean
*/
public function isEqualEnum(mixed $value, string|array $toBeEqual): bool
{
if ($value === null) {
return false;
}

if (is_string($value)) {
$enumValue = $value;
} elseif (is_object($value) && property_exists($value, 'value')) {
$enumValue = $value->value;
} else {
return false;
}

if (is_string($toBeEqual)) {
return $enumValue === $toBeEqual;
}

return in_array($enumValue, $toBeEqual, true);
}
/**
* Useful for Filament forms that sometimes parse enums as strings and sometimes as objects.
*
* @param mixed $value
* @param string|list<string> $toBeEqual
* @return boolean
*/
public function isEqualEnum(mixed $value, string|array $toBeEqual): bool
{
if ($value === null) {
return false;
}

if (is_string($value)) {
$enumValue = $value;
} elseif (is_object($value) && property_exists($value, 'value')) {
$enumValue = $value->value;
} else {
return false;
}

if (is_string($toBeEqual)) {
return $enumValue === $toBeEqual;
}

return in_array($enumValue, $toBeEqual, true);
}
And then in my form I inject the service with the boot() method :
private FilamentService $service;

public function boot(FilamentService $service): void
{
$this->service = $service;
}
private FilamentService $service;

public function boot(FilamentService $service): void
{
$this->service = $service;
}
And use it like that :
Components\Select::make('ethnicity')
->native(false)
->searchable()
->allowHtml()
->options(EthnicityEnum::selectHtmlOptions())
->default(EthnicityEnum::EUROPEAN_WHITE)
->live(debounce: 1000)
->required(),
Components\TextInput::make('ethnicity_details')
->visible(fn (Get $get) => $this->service->isEqualEnum($get('ethnicity'), [EthnicityEnum::OTHER->value]))
->required(),
Components\Select::make('ethnicity')
->native(false)
->searchable()
->allowHtml()
->options(EthnicityEnum::selectHtmlOptions())
->default(EthnicityEnum::EUROPEAN_WHITE)
->live(debounce: 1000)
->required(),
Components\TextInput::make('ethnicity_details')
->visible(fn (Get $get) => $this->service->isEqualEnum($get('ethnicity'), [EthnicityEnum::OTHER->value]))
->required(),
In this example, the ethnicity_details field is visible only if ethnicity value is EthnicityEnum::OTHER. If you want to support ->multiple() selects, you could even do it using array_intersect() :
public function isEqualEnum(mixed $value, array $toBeEqual): bool
{
if ($value === null) {
return false;
}

if (is_array($value)) { // Here, we handle the case !
return count(array_intersect($value, $toBeEqual)) > 0;
}

if (is_string($value)) {
$enumValue = $value;
} elseif (is_object($value) && property_exists($value, 'value')) {
$enumValue = $value->value;
} else {
return false;
}

return in_array($enumValue, $toBeEqual, true);
}
public function isEqualEnum(mixed $value, array $toBeEqual): bool
{
if ($value === null) {
return false;
}

if (is_array($value)) { // Here, we handle the case !
return count(array_intersect($value, $toBeEqual)) > 0;
}

if (is_string($value)) {
$enumValue = $value;
} elseif (is_object($value) && property_exists($value, 'value')) {
$enumValue = $value->value;
} else {
return false;
}

return in_array($enumValue, $toBeEqual, true);
}

Did you find this page helpful?