Deleted Records by Muhammad Amjad
CommunityA recycle bin for your Filament panel that lists records kept by spatie/laravel-deleted-models, previews their stored attributes, and restores or permanently deletes them, one at a time or in bulk.
Author:
Muhammad Amjad
Package health
Automated checks of this plugin's Composer package
15 checks
- Skipped: GitHub Actions pinned to SHA
- Skipped: GitLab CI includes pinned to SHA
- Passed: Open security advisories
- Passed: Dependabot PR responsiveness — No open Dependabot PRs.
- Skipped: Renovate MR responsiveness
- Skipped: Dependabot or Renovate configured
- Skipped: Dependency update cooldown configured
- Failed: Provides a security policy — View details on Plumb
- Passed: Abandoned or archived — No consulted source marks the package abandoned (packagist, github).
- Passed: Commit and release recency — Active: last commit 0 days ago; last release 0 days ago.
-
Passed:
composer.lock not committed by library
—
composer.lockis absent from the released dist archive. - Passed: Dist archive is lean
-
Passed:
Current Laravel version supported
—
Package dependencies resolve together with current Laravel
13.0. -
Passed:
Current PHP version supported
—
Constraint
^8.3supports current PHP8.5. - Skipped: Current Symfony version supported
filament/
namespace. Review the source and install at your own risk. Found
malware or an unresolved security issue the author won't
address?
Report it
.
Documentation
- What it does
- Requirements
- Installation
- Configuration
- Permissions
- Testing
- Changelog
- Support
- Need this customised, or something built?
- More packages
- Credits
- License
A recycle bin for your Filament panel. It lists every record that spatie/laravel-deleted-models kept when it was deleted, shows the stored attributes, and lets you restore a record or delete it permanently, one at a time or in bulk. It works with Filament 4 and 5.
Docs: amjad.com.pk/filament/plugins/filament-deleted-records

#What it does
spatie/laravel-deleted-models copies a model's attributes to a deleted_models table when you delete it, and gives
you Model::restore($key) in code. This plugin adds the screen for it:
- One list of everything deleted, newest first. Each row shows a readable title (the first of
name,title,label,subject,email,number,referenceorslugthat is filled), the key, the model type, who deleted it and when. - Preview before you restore. A modal shows every stored attribute and whether a restore can work. It warns you if a
row with that key is still in the table (soft-deleted rows count, because the key is taken), or if the model class
no longer exists. The warning does not disable Restore: a restore that fails is reported and the copy is kept.
Passwords, remember tokens, API tokens and two-factor secrets are masked on screen. They are still stored in
deleted_models, because the base package keeps hidden attributes so a restore is complete. - Restore puts the record back with the same key and attributes. It goes through the model's own
restore(), so yourbeforeRestoringModelandafterRestoringModelhooks and the package's events run. When the panel has a resource for the model, the notification links to the restored record. - Delete permanently removes the stored copy for good.
- Bulk restore and bulk permanent delete. The notification counts what was done, what failed (for example, the key is taken; the copy is kept) and what was skipped because the user may not touch it.
- Filters by model type, by who deleted it and by date range. Search matches the key exactly, and the title
attributes (see
titleAttributes()) case-insensitively. Other stored attributes are not searched, so masked values cannot be probed through search. - Who deleted it. An optional migration adds a
deleted_by_idcolumn, and the plugin fills it with the user signed in to the panel's guard when a copy is written. Deletes from queued jobs, console commands and scheduled tasks have no signed-in user, so the column stays empty for them. A user who was later removed shows as#id. - Permissions. By default, anyone who can open the panel can list, preview, restore and permanently delete. To
narrow that, give the original model's policy
restore/forceDeletemethods, register a policy for the deleted-record model, or pass one callback. See Permissions. - Works in light and dark mode with no custom theme or build step. Translatable (English included).
- Multi-tenant panels: the page is not scoped to a tenant, so it lists every tenant's deleted records together. Use
authorizeUsing()to keep it to central admins, or extend the resource and scopegetEloquentQuery()yourself.
#Requirements
- PHP 8.3+ (PHP 8.4+ on Laravel 13, which
spatie/laravel-deleted-modelsrequires there) - Laravel 12 or 13
- Filament 4 or 5
#Installation
composer require amjadiqbal/filament-deleted-records
This also installs spatie/laravel-deleted-models. Publish and run both migrations: the deleted_models table, then
the optional deleted_by_id column. The column is what powers the "Deleted by" column and filter.
php artisan vendor:publish --tag="deleted-models-migrations"
php artisan vendor:publish --tag="filament-deleted-records-migrations"
php artisan migrate
Add the KeepsDeletedModels trait to every model whose deleted records you want to keep:
use Illuminate\Database\Eloquent\Model;
use Spatie\DeletedModels\Models\Concerns\KeepsDeletedModels;
class Invoice extends Model
{
use KeepsDeletedModels;
}
Register the plugin on your panel:
use AmjadIqbal\FilamentDeletedRecords\DeletedRecordsPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(DeletedRecordsPlugin::make());
}
A "Deleted records" page now appears in the navigation, with a badge showing how many records are in the bin.
Stored copies are kept for 365 days by default. To prune older ones, schedule Laravel's model:prune for the
DeletedModel class, as described in the
spatie/laravel-deleted-models README.
#Configuration
Every option is optional:
DeletedRecordsPlugin::make()
->navigationGroup('System')
->navigationLabel('Recycle bin')
->navigationSort(90)
->navigationBadge(false) // hide the count badge
->models([Invoice::class, Customer::class]) // only these models; empty shows all
->modelLabels([Invoice::class => 'Sales invoice'])
->titleAttributes(['number', 'name']) // attributes tried, in order, for the row title
->maskedAttributes(['iban', 'secret']) // merged with the defaults; pass false as the 2nd argument to replace them
->userTitleAttribute('email') // shown in "Deleted by" (default: name)
->resource(MyDeletedRecordResource::class); // extend the resource to change anything else
To change the tracking of who deleted a record, publish the config file:
php artisan vendor:publish --tag="filament-deleted-records-config"
return [
'track_deleted_by' => true, // set to false to stop recording deleted_by_id
'user_model' => null, // null uses the default auth guard's user model; set it if your panel uses another guard
];
#Permissions
The plugin checks each action in this order:
-
authorizeUsing(), if you pass a callback. It decides everything:DeletedRecordsPlugin::make() ->authorizeUsing(fn (string $ability, ?DeletedModel $record, ?User $user): bool => $user?->isAdmin() ?? false);$abilityis one ofviewAny,view,restore,forceDelete,restoreAnyorforceDeleteAny. -
A policy for the deleted-record model (
Spatie\DeletedModels\Models\DeletedModel, or your own class set indeleted-models.model), if you register one. It gets the same abilities. -
The original model's policy. For a record, if that policy has a method named exactly
view,restoreorforceDelete, it decides, and it receives the record as it would be restored. SoInvoicePolicy::restore()decides who can restore a deleted invoice. A policy without that method does not block the action. -
Otherwise the action is allowed. Access to the panel is then the only gate. This includes
viewAny,restoreAnyandforceDeleteAny(listing the page and showing the bulk actions) whenever neither 1 nor 2 applies.
Bulk actions also check each record, skip the ones the user may not touch, and say how many were skipped.
#Testing
composer test
scripts/ci-local.sh runs Pint, PHPStan and the tests on PHP 8.3 to 8.5, Laravel 12 and 13, and Filament 4 and 5.
The tests use SQLite; set DB_TEST_DRIVER=mysql or pgsql (with DB_PORT, DB_USERNAME, DB_PASSWORD,
DB_DATABASE) to run them on a real server.
#Changelog
See CHANGELOG.md.
#Support
- 🐛 Bug or feature request: open an issue
- 🔒 Security issue: please report it privately through GitHub's security tab
#Need this customised, or something built?
I'm available for custom development, package integration, and technical consulting.
#More packages
This is part of a family of open-source packages. See all of them.
| filament-change-requests | Hold record edits in Filament for approval, with a field-by-field diff |
| filament-url-image-uploader | Upload images to Filament from a URL |
| refresher | Restore a cached test database instead of migrate:fresh |
#Credits
- Amjad Iqbal
- Storage and restore: spatie/laravel-deleted-models by Spatie (MIT). This plugin is not affiliated with or endorsed by Spatie.
#License
The MIT License (MIT). See LICENSE.md.
The author
I am a seasoned full-stack web developer specializing in Laravel, Livewire, and Vue.js ecosystems. With extensive experience in building modern web applications, I leverage the power of Laravel's elegant syntax and robust features to create scalable backend solutions. My expertise extends to crafting responsive and interactive user interfaces using Livewire and Vue.js, enhanced by the utility-first approach of Tailwind CSS.
From the same author
Record Settings
Edit per-user, per-team and per-tenant settings in Filament from a settings page, a modal action or resource form fields, stored with glorand/laravel-model-settings or in a JSON column.
Author:
Muhammad Amjad
Uptime Monitor
An admin UI for spatie/laravel-uptime-monitor that lets you add, edit, turn on and off and check uptime and SSL certificate monitors from your Filament panel, with status filters and a dashboard widget.
Author:
Muhammad Amjad
Stat Cards
Richer stat cards for the Filament stats overview widget, adding progress bars, icon tiles, background, label and value colours, trends, badges and bar charts to the widget you already have.
Author:
Muhammad Amjad
XLSX Import
Lets Filament's own ImportAction read Excel (.xlsx) and OpenDocument (.ods) workbooks as well as CSV, with a sheet picker, while your existing Importer classes, queued jobs and failed-rows downloads keep working unchanged.
Author:
Muhammad Amjad
Featured Plugins
A selection of plugins curated by the Filament team
Noir Theme
A theme that gives panels a focused, refined look with near-black surfaces, crisp actions, and restrained color.
Filament
Soft Theme
A theme that gives panels a warm, approachable look with rounded shapes, gentle colors, and serif headings.
Filament
Advanced Tables (formerly Filter Sets)
Supercharge your tables with powerful features like user-customizable views, quick filters, multi-column sorting, advanced table searching, convenient view management, and more. Compatible with Resource Panel Tables, Relation Managers, Table Widgets, and Table Builder!
Kenneth Sese