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
18.07k stars 2.83k forks source link

`int` backed `enum` throws during render in `SelectColumn` for `Table` since the enum value can't be converted to `string` #7470

Closed bradleybernard closed 1 year ago

bradleybernard commented 1 year ago

Package

filament/filament

Package Version

v3.0.7

Laravel Version

10.17.1

Livewire Version

v3.0.0-beta.7

PHP Version

8.2.8

Problem description

Issue

When using an enum that is backed by int and used in a table, there is code in select-column.blade.php that tries to stringify the enum value, but int enums do not support __toString() unfortunately, so we might need to check explicitly for this case and manually convert the enum's value to string.

See code below, exception fires when visiting: admin/users

Exception

Object of class App\Enums\UserProviderType could not be converted to string

CleanShot 2023-08-04 at 14 14 33 CleanShot 2023-08-04 at 14 13 40

Code

UserProviderType.php:

namespace App\Enums;

use Filament\Support\Contracts\HasLabel;

enum UserProviderType: int implements HasLabel
{
    case APPLE = 1;
    case GOOGLE = 2;

    public function getLabel(): ?string
    {
        return match($this) {
            self::APPLE => 'Apple',
            self::GOOGLE => 'Google'
        };
    }
}

UserResource.php:

 public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\SelectColumn::make('provider_type')
                                    ->label('Provider')
                                    ->options(UserProviderType::class)
                                    ->sortable(),
            ]);
}

`User.php:


protected $casts = [
        'email_verified_at' => 'datetime',
        'provider_type' => UserProviderType::class,
    ];

select-column.blade.php:

<input
        type="hidden"
        value="{{ str($state)->replace('"', '\\"') }}"
        x-ref="newState"
    />

Expected behavior

Since int backed enums can easily be translated to string, we can use reflection or other mechanism to detect and provide the string value?

select-column.blade.php

@php
    $canSelectPlaceholder = $canSelectPlaceholder();
    $isDisabled = $isDisabled();
    $rawState = $getState();

    try {
        $state = str($rawState);
    } catch (\Throwable $e) {
        $reflectedEnum = new ReflectionEnum($rawState);
        $reflectedBackingType = $reflectedEnum->getBackingType();

        if ($reflectedBackingType === 'int') {
            $state = str($rawState->getValue());
        } else {
           // re-throw
        }
    }
@endphp

Oddly enough, it works on the edit page, just not on the select column in the table. The enum is hooked up roughly the same way, but it's the Select component, not the SelectColumn:

Select::make('provider_type')
                    ->label('Provider')
                    ->options(UserProviderType::class)
                    ->searchable(),

So maybe bringing some code over from Select into SelectColumn :)

Steps to reproduce

  1. Create int backed enum
  2. Hook up to form table select as direct class usage
  3. Visit url to invoke table render
  4. Exception fires

Reproduction repository

https://github.com/bradleybernard/FilamentEnumBug

Relevant log output

No response

bradleybernard commented 1 year ago

See the last commit of my repro for the more important pieces, the first 2 commits were just Laravel install, then Filament install:

https://github.com/bradleybernard/FilamentEnumBug/commit/250c062763ebb7364c0f0d24206129d461c62961

danharrin commented 1 year ago

Would appreciate a PR to fix this if you can

bradleybernard commented 1 year ago

Will do sometime this week, if not next week :) thanks Dan!

bradleybernard commented 1 year ago

Opened up https://github.com/filamentphp/filament/pull/7962 but would love some eyes on it @danharrin! :)

gotzsys commented 1 year ago

I am having a similar problem using a string type enumerator.

<?php

namespace App\Enums;

use Filament\Support\Contracts\HasLabel;

enum WaitlistStatus: string implements HasLabel
{
  case WAITING = 'waiting';
  case CALLED = 'called';

  public function getLabel(): ?string
  {
    return match ($this) {
      self::WAITING => 'Na fila',
      self::CALLED => 'Chamado',
    };
  }
}
@php
    $canSelectPlaceholder = $canSelectPlaceholder();
    $isDisabled = $isDisabled();
    $state = $getState();
    dd($state);
@endphp
App\Enums\WaitlistStatus {#3167 ▼ // vendor/filament/tables/src/../resources/views/columns/select-column.blade.php
  +name: "WAITING"
  +value: "waiting"
}

Returns the error: Object of class App\Enums\WaitlistStatus could not be converted to string

Making this change works... but obviously, the solution must be more complex. image