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.2k stars 2.84k forks source link

Table filter using a select on a relationship with a column based on enum class #12948

Open stevefontaine opened 4 months ago

stevefontaine commented 4 months ago

Package

filament/filament

Package Version

v3.2.82

Laravel Version

v11.8.0

Livewire Version

3.5.0

PHP Version

PHP 8.2.0

Problem description

I get this error: Filament\Forms\Components\Select::isOptionDisabled(): Argument #2 ($label) must be of type string, App\Enums\MembershipStatuses given

From vendor/filament/forms/src/Components/Concerns/CanDisableOptions.php#39

Expected behavior

Should get a table filtered with the relationship enum column when accessing the admin panel Membership Request resource

php artisan migrate:fresh --seed

to populate with a few rows and access the panel /admin with user admin@admin.com password="password"

Steps to reproduce

I have a model MembershipRequest which have a BelongsTo relationship to a Membership class The Membership class has a property 'status' which is casts to an Enum Class called MembershipStatuses

I add the filter to the filament table for resource MembershipRequest using a relationship() method to the membership status:

SelectFilter::make('membership.status')
    ->relationship('membership', 'status'),

My Model Membership has this $casts

protected $casts = [
        'status' => App\Enums\MembershipStatuses::class, // Enum class
        //...    
];

And MembershipRequest has this relationship:

    public function membership(): BelongsTo
    {
        return $this->belongsTo(Membership::class);
    }

Reproduction repository

https://github.com/ngalso/filament-issue.git

Relevant log output

No response

Donate 💰 to fund this issue

Fund with Polar

RoyVogel commented 4 months ago

Use v3.2.78 fixed the problem for me. array_diff(): argument #2 must be of type array, string given in update forms with relationships.

stevefontaine commented 4 months ago

I am already using v.3.2.82 and I just tried with v3.2.83 and the error is still being triggered.

RoyVogel commented 4 months ago

Downgrade te version: "filament/filament": "v3.2.78",

schmeits commented 4 months ago

A fix is to add the HasLabel contract in the isOptionDisabled $label parameter (Filament\Forms\Components\Concerns\CanDisableOptions)

public function isOptionDisabled($value, string|\Filament\Support\Contracts\HasLabel $label): bool
{
   //
}

Only thing left from there is to use the HasLabel contract in your enum

<?php
namespace App\Enums;

use Filament\Support\Contracts\HasLabel;

enum MembershipStatuses: string implements HasLabel
{
    case UNPAID = 'unpaid'; // not yet paid
    case PROCESSING = 'processing'; // paid and need to print for board
    case PENDING = 'pending'; // printed and waiting to be approved by board - no vote possible
    case ACTIVE = 'active'; // approved by board - voting is possible
    case EXPIRING = 'expiring'; // no vote possible - but renewing is possible
    case EXPIRED = 'expired'; // no vote - no renewal. Need a new one.
    case CANCELLED = 'cancelled'; // cancelled by the user
    case REVOKED = 'revoked'; // revoked by the board - no new approval possible

    public function getLabel(): ?string
    {
        return $this->name;
    }
}
schmeits commented 4 months ago

If this is a workable solution I can create a PR?

stevefontaine commented 4 months ago

Thanks, let me run some test and will let you know.

stevefontaine commented 4 months ago

It now passes the error message but the filter is not applied to the resource

stevefontaine commented 4 months ago

Downgrade te version: "filament/filament": "v3.2.78",

Thanks but even on v3.2.78 the error still happens

schmeits commented 4 months ago

Hi Steve,

One more change to get it to work

In the SelectFilter class in Filament\Tables\Filters in the SetUp function you have to add this after line 112

 if ($label instanceof HasLabel) {
                $label = $label->getLabel();
            }

Then it will work

Don't forget to change your filter to

->filters([
      SelectFilter::make('status')
          ->relationship('membership', 'status')
          ->preload(),
  ])
  ->filtersFormSchema(fn(array $filters): array => [
      Section::make('Filters')
          ->description('These filters affect the visibility of the records in the table.')
          ->schema([
              $filters['status'],
          ])
          ->columns(2)
          ->columnSpanFull(),
  ])

changed membership.status to status

AidasK commented 3 weeks ago

Solution for people who has upgraded to laravel 11 and is still waiting for this to be fixed:

Filament\Forms\Components\Select component fix

Select::make('payment_processor_id')
   ->relationship('paymentProcessor', 'name')
    // add this line
    ->getOptionLabelFromRecordUsing(fn (PaymentProcessor $record) => $record->name->getLabel())

Filament\Tables\Filters\SelectFilter component fix

SelectFilter::make('transport_order_status_id')
   ->label('Status')
   ->relationship('status', 'name')
   // add this line
   ->getOptionLabelFromRecordUsing(fn (TransportOrderStatus $record) => $record->name->getLabel())
   // and this
   ->indicateUsing(fn ($data) => 'Status: ' . implode(' & ', $data['values']))

Looks like no other has encountered issues with indicators, but they are crashing too.

manassegitau commented 1 week ago

Thank You very much for this man. I spent a lot of time trying to figure it out. Finally this worked for me!