konnco/filament-safely-delete
A Filament plugin that makes deleting records safer by adding confirmation safeguards to the delete action, helping prevent accidental data loss in your admin panels. Quick to install and easy to drop into existing resources and tables.
Minimal path from install to first safe delete:
0.3.2, which added Laravel 10 support but did not add Filament v3 support. In composer.json you need illuminate/support/laravel/framework ^10 and an filament/filament constraint still in the v2 range. Laravel 10 + Filament v2 is now a supported combination; Laravel 10 + Filament v3 is not. If you're on Filament v3+, budget for a fork or the copy-the-class route (see Gotchas).composer require konnco/filament-safely-delete:^0.3.2
php artisan vendor:publish --tag="filament-safely-delete-config"
php artisan vendor:publish --tag="filament-safely-delete-views"
Filament::registerPlugin(...) in a service provider or the config/filament.php plugins array. The v3 Panel::make()->plugin(...) fluent style is not what this package targets.Illuminate\Database\Eloquent\SoftDeletes and a migration containing $table->softDeletes();.Where to look first: the README for exact action/trait names, then src/ — the package is small enough that the action classes and any trait are the real documentation. Check config/filament-safely-delete.php (if published) for default behaviour around force-delete visibility and confirmation modals.
First use case: add the package's delete action to a resource table so "delete" soft-deletes the record, and expose Restore / Force Delete as separate, permission-gated actions. That's the 80% case and the fastest way to validate the package fits your Filament version.
Resource table actions. Drop the package action into getTableActions()/getTableBulkActions() alongside Filament's built-ins:
->actions([
SafelyDeleteAction::make(), // soft delete + confirmation
RestoreAction::make(), // only visible when trashed
ForceDeleteAction::make(), // gated by policy
])
Use the trait the package provides (commonly applied to the Resource) rather than reimplementing the delete/restore wiring by hand — that's where the confirmation modal, notification, and redirect behaviour live.
Bulk actions are the risky surface. Mass delete via checkbox selection is where you'll actually feel the package: verify it chunks large selections rather than issuing one whereIn(...)->delete(), and check whether bulk force-delete is exposed at all. For anything over a few hundred rows, add your own ->action() with chunkById() so you control timeouts and transaction scope.
Policy wiring is the integration point, not the package. Define delete, restore, and forceDelete on the model policy and let Filament's authorization hide the actions. This is cleaner than conditionally registering actions per role, and it means the package's actions inherit your existing security model instead of duplicating it.
Relation managers. The same action works inside RelationManager tables for child records, but a soft delete on the parent does not cascade to children — if your domain requires children to disappear with the parent, override deleting/deleted on the model or handle it in a service class called from a custom action.
Restore UIs need a trashed-aware query. Any table or widget that lets users restore records must bypass the SoftDeletes global scope. Build a dedicated "Trash" resource or page using ->query(Model::onlyTrashed()) and keep it separate from the normal list — mixing trashed and live records in one table confuses filtering and sorting.
Custom actions. Extend the package's action class (or wrap it) rather than editing vendor files. Typical extensions: custom modal copy, ->requiresConfirmation() with a typed confirmation field for destructive operations, post-delete redirect, and a success notification that includes the model label.
Testing. Assert with assertSoftDeleted($model) / assertNotSoftDeleted($model) in feature tests, and Livewire-test the action with livewire(ManagePosts::class)->callTableAction(SafelyDeleteAction::class, $post). As of 0.3.2 the package skips its own validation when the app env is testing, so Livewire action tests exercise the happy path only — test invalid-input handling in a non-testing env or by asserting against your own validation layer. Test the policy paths separately; most "action missing" bugs are authorization, not the package.
0.3.2 added Laravel 10 support and a new contributor, so the "abandoned since Feb 2023" framing is out of date, and Laravel 10 apps still on Filament v2 are now a first-class target. What did not change is Filament v3 support: the action API and namespaces are still v2-era. Pin ^0.3.2, and on Filament v3 expect breakage — the action surface is small enough that copying the class into app/Filament/Actions/ is often lower risk than a fork.APP_ENV=testing. The 0.3.2 change means the package's guarded path is bypassed in tests, so a green suite proves nothing about validation or confirmation enforcement. If validation matters to you, add a test that runs the action with the env forced to something else, or assert on your own wrapper instead of the package's internals.->visible() conditions, and whether the table query is scoped such that the record is even present. canDelete() returning false silently removes the action.deleting hooks in subtle ways. Model events still fire (forceDeleting/forceDeleted), but DB-level ON DELETE CASCADE constraints do apply on force delete and do not apply on soft delete. Any cleanup logic keyed to deleting will not run for soft deletes.email unique + soft delete means a "deleted" user still blocks re-registration. Either use a partial/composite unique index including deleted_at, or restore-then-update instead of delete.php artisan config:clear — a stale cached config silently reverts behaviour and is a classic wasted afternoon.Log::info() or an audit table entry in a wrapper action with auth()->id() and the model key. Soft-deleted rows retain no record of who deleted them, which matters the first time someone asks.$listeners and whether the package's action name collides with a built-in DeleteAction already registered on the same table — duplicate action names cause silent no-ops.vendor/.How can I help you explore Laravel packages today?