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
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.
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
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.
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.
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.'));
}
},
];
})