Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

peterhrobar's avatar

beforeStateUpdated hook in ToggleColumns

How can I prevent Filament 3.0 from saving the new state in the database if I have the following beforeStateUpdated hook in my resource file:

Tables\Columns\ToggleColumn::make('is_active')
                    ->label('Active?')
                    ->beforeStateUpdated(function ($record, $state) {
                        if ($state &&
                            $record->validity_date !== null &&
                            $record->validity_date < now()) {
                                Notification::make()
                                    ->title('The approver can not be active if the validity date is in the past')
                                    ->danger()
                                    ->duration(5000)
                                    ->send();
                                return false;
                            }
                      
                    }),

Adding return false does NOT seem to cause any difference even if we consider that the is_active property of the relevant model is casted to a boolean value

0 likes
5 replies
LaryAI's avatar
Level 58

To prevent Filament from saving the new state in the database when using the beforeStateUpdated hook, you should throw an exception instead of returning false. This will interrupt the update process and prevent the state from being saved. Here's how you can modify your beforeStateUpdated hook:

Tables\Columns\ToggleColumn::make('is_active')
    ->label('Active?')
    ->beforeStateUpdated(function ($record, $state) {
        if ($state &&
            $record->validity_date !== null &&
            $record->validity_date < now()) {
                Notification::make()
                    ->title('The approver can not be active if the validity date is in the past')
                    ->danger()
                    ->duration(5000)
                    ->send();

                // Throw an exception to prevent the state from being saved
                throw new \Exception('The approver can not be active if the validity date is in the past');
            }
    }),

By throwing an exception, you ensure that the update process is halted, and the user is informed about the reason through the notification you've set up. Make sure to catch this exception appropriately if needed, to handle the user experience smoothly.

peterhrobar's avatar

Is there an easy way to add a try catch block to the evaluate method of the EvaluatesClosures concern. It seems Filament does not have a standard Exception hander

aurawindsurfing's avatar

Hey @peterhrobar

In filament you have: https://filamentphp.com/docs/3.x/panels/resources/creating-records#lifecycle-hooks

And in particular:

$this->halt();

which you can find here: https://filamentphp.com/docs/3.x/panels/resources/creating-records#halting-the-creation-process

I did not see

->beforeStateUpdated

Available for toggle column so I do not think you can use it.

ANOTHER WAY

The native laravel way would be to use observer on your model: https://laravel.com/docs/master/eloquent#observers

Apart from what you see in the example there are also other events:

Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, trashed, forceDeleting, forceDeleted, restoring, restored, and replicating.

Hope it helps!

1 like
peterhrobar's avatar

Thank you!

Though the model policy contains a deleteAny method but that does not get the collection, only the current user instance. This is what Filament checks for bulk actions.

I decided to handle my pre-check in the action itself with a good old fashioned if...then ...:-)

BTW the visible and hidden methods are kind of strange inside of bulk actions therefore these were also not considered.

CoringaWc's avatar

I was able to prevent the ToggleColumn from deactivating by using a Closure inside the rules() method. This allowed me to check if the user is the only admin and cancel the toggle action accordingly, showing a notification to the user.

use Filament\Tables;
use Filament\Notifications\Notification;

Tables\Columns\ToggleColumn::make('isAppAdmin')
    ->label('Administrator')
    ->alignCenter()
    ->translateLabel()
    ->rules(function (User $record) {
        return [
            'boolean',
            function (string $attribute, mixed $value, Closure $fail) use ($record) {
                if (
                    $record->isAdmin('app') &&
                    $record->tenant?->users()->admins('app')->whereNot('id', $record->id)->count() === 0
                ) {
                    Notification::make()
                        ->title(__('You cannot remove the unique administrator of the agency.'))
                        ->body(__('At least one administrator is required for the agency.'))
                        ->danger()
                        ->send();

                    $fail(__('You cannot remove the unique administrator of the agency.'));
                }
            },
        ];
    })

Please or to participate in this conversation.