If this plugin is useful to you, consider giving it a â on GitHub.
Revive is a plugin for FilamentPHP that brings a central Recycle Bin to your application. It lets you restore or permanently delete soft-deleted Eloquent models in just a few clicks.
This plugin is especially useful for SaaS applications, admin dashboards, or any multi-user platform where recovering accidentally deleted data is important.

Install the latest version for Filament v5:
composer require promethys/revive
php artisan revive:install
Requirements: PHP 8.2+, Laravel 11+, Filament v5
If you need to install V2 for Filament v4:
composer require promethys/revive:^2.0
php artisan revive:install
If you need to install V1 for Filament v3:
composer require promethys/revive:^1.0
php artisan revive:install
If you prefer to manually publish and run the migrations:
php artisan vendor:publish --tag="revive-migrations"
php artisan migrate
If you're currently using V2 and want to upgrade to V3 for Filament v5:
# 1. Ensure you have PHP 8.2+, Laravel 11+, and Filament v5
# 2. Update your composer constraint
composer require promethys/revive:^3.0
# 3. Clear caches
php artisan config:clear
php artisan cache:clear
Note: V3 has no database schema changes from V2. The upgrade primarily involves compatibility with Filament v5's API changes.
If you're currently using V1 and want to upgrade to V2:
# 1. Update your composer constraint
composer require promethys/revive:^2.0
# 2. Publish and Run new migrations
php artisan vendor:publish --tag="revive-migrations"
php artisan migrate
# 3. Update your plugin configuration (see Configuration section below)
Register the plugin in each panel where you want the recycle bin available:
use Promethys\Revive\RevivePlugin;
$panel->plugins([
RevivePlugin::make()
]);
You can also customize the plugin using fluent configuration:
use Promethys\Revive\RevivePlugin;
$panel->plugins([
RevivePlugin::make()
->authorize(auth()->user()->isAdmin()) // Accepts a boolean or Closure to control access
->navigationGroup('Settings') // Group the page under a custom sidebar section
->navigationIcon('heroicon-o-archive-box-arrow-down')
->activeNavigationIcon('heroicon-o-archive-box-arrow-down')
->navigationSort(1)
->navigationLabel('Custom Label')
->title('Custom Title')
->slug('custom-slug')
]);
Revive provides powerful scoping features that allow users to see only their own deleted items or items within their tenant/organization:
use Promethys\Revive\RevivePlugin;
// Basic user-scoped recycle bin (users only see their own deleted items)
$panel->plugins([
RevivePlugin::make()
->enableUserScoping() // Default: true
->enableTenantScoping(false)
]);
// Multi-tenant recycle bin
$panel->plugins([
RevivePlugin::make()
->enableTenantScoping() // Default: true
->enableUserScoping(false)
]);
// Admin panel - see all deleted items
$panel->plugins([
RevivePlugin::make()
->showAllRecords() // Shows all records regardless of user/tenant
->authorize(fn () => auth()->user()->isAdmin())
]);
// Custom model filtering
$panel->plugins([
RevivePlugin::make()
->models([Post::class, Comment::class]) // Only show these models
->enableUserScoping()
]);
// Completely disable scoping (like v1 behavior)
$panel->plugins([
RevivePlugin::make()
->withoutScoping()
]);
User Panel Configuration:
// User panel - users only see their own deleted items
public function panel(Panel $panel): Panel
{
return $panel
->id('user')
->plugins([
RevivePlugin::make()
->navigationGroup('My Account')
->navigationLabel('My Deleted Items')
->title('My Recycle Bin')
->enableUserScoping(true)
->enableTenantScoping(false)
]);
}
Admin Panel Configuration:
// Admin panel - see all deleted items across all users/tenants
public function panel(Panel $panel): Panel
{
return $panel
->id('admin')
->plugins([
RevivePlugin::make()
->navigationGroup('Administration')
->navigationLabel('Global Recycle Bin')
->title('All Deleted Items')
->showAllRecords()
->authorize(fn () => auth()->user()->isAdmin())
]);
}
Tenant Panel Configuration:
// Tenant panel - see deleted items for current tenant only
public function panel(Panel $panel): Panel
{
return $panel
->id('tenant')
->tenant(Team::class)
->plugins([
RevivePlugin::make()
->navigationGroup('Team Management')
->navigationLabel('Team Recycle Bin')
->title('Team Deleted Items')
->enableTenantScoping()
->enableUserScoping(false) // All team members can see all team deletions
]);
}
â ī¸ The plugin currently supports only models in the
App\Modelsnamespace. If you want to register a third-party model (e.g., from another package), create a wrapper class that extends it and add theRecyclabletrait there:
namespace App\Models;
use Promethys\Revive\Concerns\Recyclable;
use Vendor\Package\Models\Foo as BaseFoo;
class Foo extends BaseFoo
{
use SoftDeletes;
use Recyclable;
}
Once the plugin is installed and configured, you'll see a new page in your Filament navigation menu.
From there, users can restore deleted data or permanently remove it.
Recyclable trait to any soft-deletable modeluse Promethys\Revive\Concerns\Recyclable;
class Post extends Model
{
use SoftDeletes;
use Recyclable;
}
âšī¸ Important: Adding the
Recyclabletrait without usingSoftDeleteswill throw an exception.
Override these methods in your models to customize how users and tenants are detected:
class Post extends Model
{
use SoftDeletes, Recyclable;
/**
* Get the user who should be recorded as deleting this model
* This would override the default method
*/
public function getDeletedByUser()
{
// Custom logic - maybe you store it in a different field
return $this->deleted_by_user_id ?? auth()->id();
}
/**
* Get the tenant ID for this model
* This would override the default method
*/
public function getTenantId()
{
// For teams/organizations
return $this->organization_id;
// Or for complex tenant relationships
return $this->workspace->tenant_id ?? null;
}
}
Filament Multi-Tenancy Integration: The plugin automatically detects Filament tenancy:
// In your panel service provider
$panel->plugins([
RevivePlugin::make()
->enableTenantScoping() // Automatically uses filament()->getTenant()
]);
Custom Tenant Models:
class Post extends Model
{
use SoftDeletes, Recyclable;
public function getTenantId()
{
// For Filament Multi-tenancy
return filament()->getTenant()->id ?? null;
// For custom team-based tenancy
return $this->team_id;
// For organization-based tenancy
return $this->organization_id;
}
}
If you already have soft-deleted records before installing the plugin, you can "discover" them by running:
php artisan revive:discover-soft-deleted
This command will:
Recyclable traitPreview changes without making them:
php artisan revive:discover-soft-deleted --dry-run
Include user/tenant scoping information:
php artisan revive:discover-soft-deleted --with-scope
This option attempts to determine who deleted each record and includes tenant information.
Discover records for a specific model:
php artisan revive:discover-soft-deleted --model=Product
# or use the full class name
php artisan revive:discover-soft-deleted --model="App\Models\Shop\Product"
Combine options:
php artisan revive:discover-soft-deleted --model=Category --dry-run --with-scope
$ php artisan revive:discover-soft-deleted --with-scope
Discovering soft-deleted records...
đ Scanning Category...
No soft-deleted records found.
đ Scanning Comment...
â
0/3 records discovered
đ Scanning Brand...
â
8/8 records discovered
đ Scanning Category...
â
2/2 records discovered
đ Scanning Customer...
â
0/1 records discovered
đ Scanning Order...
No soft-deleted records found.
đ Scanning Product...
â
12/15 records discovered
đ User/tenant scoping information was included
⨠Discovery completed:
âĸ 29 total soft-deleted records scanned
âĸ 22 new records discovered and added to recycle bin
đĄ Tips:
- Run the command with
--dry-runfirst to preview what will be discovered, especially on production systems with large amounts of existing data.- Use
--with-scopewhen upgrading from V1 to V2 to include user/tenant information for existing records.- For large systems with extensive output, consider redirecting the command output to a file:
php artisan revive:discover-soft-deleted > discovery-results.txt
You don't have to register the plugin in your panel to use the table.
Instead, you can render the Livewire component directly in a Blade view:
@livewire(\Promethys\Revive\Tables\RecycleBin::class)
<!-- User-scoped recycle bin -->
@livewire(\Promethys\Revive\Tables\RecycleBin::class, [
'user' => auth()->user(),
'enableUserScoping' => true,
'enableTenantScoping' => false,
])
<!-- Admin view - all records -->
@livewire(\Promethys\Revive\Tables\RecycleBin::class, [
'showAllRecords' => true,
])
<!-- Tenant-specific recycle bin -->
@livewire(\Promethys\Revive\Tables\RecycleBin::class, [
'tenant' => filament()->getTenant(),
'enableTenantScoping' => true,
'enableUserScoping' => false,
])
<!-- Specific models only -->
@livewire(\Promethys\Revive\Tables\RecycleBin::class, [
'models' => [App\Models\Post::class, App\Models\Comment::class],
'user' => auth()->user(),
])
This is ideal if:
The plugin allows you to fully customize both the RecycleBin page and RecycleBin table by extending the base classes and registering your custom implementations.
You can extend the RecycleBin table component to customize columns, filters, actions, and more.
Create a class that extends Promethys\Revive\Tables\RecycleBin:
<?php
namespace App\Livewire;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Promethys\Revive\Tables\RecycleBin as BaseRecycleBinTable;
class CustomRecycleBinTable extends BaseRecycleBinTable
{
// Customize table columns
protected function getTableColumns(): array
{
return [
...parent::getTableColumns(), // Include default columns
// Add your custom columns
TextColumn::make('tenant_id')
->label('Tenant')
->sortable(),
];
}
// Customize table filters
protected function getTableFilters(): array
{
return [
...parent::getTableFilters(), // Include default filters
// Add your custom filters
SelectFilter::make('deleted_by')
->label('Deleted By')
->searchable()
->multiple(),
];
}
// Customize record actions
protected function getTableRecordActions(): array
{
// Option 1: Extend default actions
return [
...parent::getTableRecordActions(),
// Add custom actions here
];
// Option 2: Completely replace default actions
return [
// Your custom actions only
];
}
// Customize bulk actions
protected function getTableToolbarActions(): array
{
return [
...parent::getTableToolbarActions(),
// Add custom bulk actions
];
}
}
In your panel provider, register the custom table using registerTable():
use App\Livewire\CustomRecycleBinTable;
use Promethys\Revive\RevivePlugin;
$panel->plugins([
RevivePlugin::make()
->registerTable(CustomRecycleBinTable::class)
]);
| Method | Description |
|---|---|
getTableColumns() |
Define the table columns |
getTableFilters() |
Define table filters |
getTableRecordActions() |
Define actions for each row (view, restore, delete) |
getTableToolbarActions() |
Define bulk actions (toolbar actions) |
getTableHeaderActions() |
Define header actions |
getQuery() |
Customize the base query |
restoreModel($record) |
Customize restore behavior |
forceDeleteModel($record) |
Customize force delete behavior |
You can extend the RecycleBin page to customize its appearance, layout, or behavior.
Create a class that extends Promethys\Revive\Pages\RecycleBin:
<?php
namespace App\Filament\Pages;
use Promethys\Revive\Pages\RecycleBin as BaseRecycleBinPage;
class CustomRecycleBinPage extends BaseRecycleBinPage
{
// Use a custom view
protected string $view = 'filament.pages.custom-recycle-bin';
// Override page methods as needed
public static function getNavigationBadge(): ?string
{
// Add a badge showing count of deleted items
return RecycleBinItem::count();
}
}
If you specified a custom view, create it at resources/views/filament/pages/custom-recycle-bin.blade.php:
<x-filament-panels::page>
{{-- Custom header or content --}}
<div class="mb-4">
<h2 class="text-xl font-bold">Custom Recycle Bin Header</h2>
<p class="text-gray-600">Manage your deleted records here.</p>
</div>
{{-- Render the table --}}
@livewire($this->recycleBinComponent, $this->componentParams)
{{-- Custom footer or additional content --}}
<div class="mt-4 text-sm text-gray-500">
Remember to permanently delete old records regularly!
</div>
</x-filament-panels::page>
In your panel provider, register the custom page using registerPage():
use App\Filament\Pages\CustomRecycleBinPage;
use Promethys\Revive\RevivePlugin;
$panel->plugins([
RevivePlugin::make()
->registerPage(CustomRecycleBinPage::class)
]);
For advanced customization, you can override the restore and delete logic:
use Illuminate\Support\Facades\Log;
class CustomRecycleBinTable extends BaseRecycleBinTable
{
protected function restoreModel($record)
{
// Add custom pre-restore logic
Log::info("Restoring {$record->model_type}#{$record->model_id}");
// Perform the restore
$result = parent::restoreModel($record);
// Add custom post-restore logic
if ($result) {
// Send notification, update cache, etc.
}
return $result;
}
protected function forceDeleteModel($record)
{
// Add custom pre-delete logic
$this->cleanupRelatedData($record);
// Perform the deletion
return parent::forceDeleteModel($record);
}
private function cleanupRelatedData($record)
{
// Your custom cleanup logic
}
}
Always ensure proper authorization:
RevivePlugin::make()
->authorize(function () {
return auth()->user()->can('view-recycle-bin'); // Or any other logic. Ensure you return a boolean.
});
If you use the Tenant scoping of the plugin, please check the Filament Multi-tenancy security section to understand the security implications of multi-tenancy and how to properly implement it.
If you encounter a bug or unexpected behavior, please help us help you by following these guidelines:
The more details you provide, the faster and better we can help. Thank you!
See CONTRIBUTING for guidelines.
This project is open-sourced under the MIT license.
See LICENSE.md for more details.
How can I help you explore Laravel packages today?