fetzi.dev

Using action classes for table actions in Filament 3

2 minutes

I recently switched a private project from Laravel Nova to Filament 3 and had the problem that defining custom table actions was complicated and the produced code was unreadable, at least for me.

I was spoiled by Nova because it provides Action classes that contain the actual action logic and also the action configuration such as label, name etc.

Now lets look at some example code from Filament 3:

// ... table definitions
->actions([
    Action::make('send-booking-confirmation')
        ->action(fn (Booking $booking) => (new SendBookingConfirmation)($booking))
        ->label('Send confirmation'),
]);

As you can see there is quite a lot of code necessary in the the resource class to register a custom action. IMHO this configuration should be boxed inside an action class, especially if the action is reused in multiple resources.

At first I thought about the interface of the action class. Inside the actions definition of my resource I basically wanted the following syntax:

->actions([
    SendConfirmation::make(),
    SampleAction::make(),
]);

To be able to provide this interface the following class calles BaseAction needs to be defined:

namespace App\Filament\Actions;

use Filament\Tables\Actions\Action;

class BaseAction
{
    protected $name = null;

    protected $label = null;

    public static function make() : Action
    {
        $instance = app(static::class);

        return Action::make($instance->name)
            ->label($instance->label)
            ->action(fn($record) => $instance($record));
    }
}

IMPORTANT: the parameter name of the callback function MUST be called $record, otherwise injecting the model instance won’t work.

Based on this class we now can create action classes that can be registered inside a Resource by calling the make function.

namespace App\Filament\Actions;

use App\Models\Booking;
use Filament\Notifications\Notification;

class SendBookingConfirmation
{
    protected $name = 'send-booking-confirmation';

    protected $label = 'Send confirmation';

    public function __invoke(Booking $booking)
    {
        // action logic
    }
}

Conclusion

This approach is an opinionated one but it can simplify the registration of custom actions throughout your filament application. Based on the provided approach you can easily simplify the registration even further by removing the definition of the name and providing a name generation logic based on the class name.

Do you enjoy reading my blog?

sponsor me at ko-fi