Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Revive Laravel Package

promethys/revive

Revive adds a central Recycle Bin to FilamentPHP apps, letting you view, restore, or permanently delete soft-deleted Eloquent models in a few clicks. Register models with a trait, filter by type, and recover deleted data fast.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up in under five minutes. Revive is a Filament panel plugin, so the setup is two commands and two code changes:

composer require promethys/revive
php artisan revive:install   # publishes + runs migrations

Then register the plugin in the panel provider you want the Recycle Bin to appear in:

use Promethys\Revive\RevivePlugin;

$panel->plugins([
    RevivePlugin::make(),
]);

Finally, make a model recyclable by adding the trait alongside SoftDeletes:

use Illuminate\Database\Eloquent\SoftDeletes;
use Promethys\Revive\Concerns\Recyclable;

class Post extends Model
{
    use SoftDeletes;
    use Recyclable;
}

Where to look first. The RevivePlugin::make() fluent API is the whole control surface — everything else (authorize, navigationGroup, slug, scoping toggles, modelsNamespace) hangs off it. Start with a bare plugin registration, confirm the nav item appears, then layer configuration. If your models aren't in App\Models, jump straight to ->modelsNamespace(...) before you debug why nothing shows up.

First use case you'll actually ship. A soft-delete safety net for an admin panel: register the plugin, tag your Post/Comment/Order models with Recyclable, and you immediately get a page listing deleted records with restore and force-delete actions, plus a model-type filter and free-text search. That is the 80% case and it requires no custom table code.

Existing data. If you already have soft-deleted rows from before installing (very common — you had SoftDeletes before you had Revive), they won't appear until you run discovery:

php artisan revive:discover-soft-deleted --dry-run   # preview first
php artisan revive:discover-soft-deleted --with-scope

--with-scope is worth using explicitly: it backfills the user/tenant attribution for records deleted before scoping existed, which otherwise renders the scoping filters useless on historical rows.

Implementation Patterns

Panel-specific scoping is the core design decision. Revive gives you three knobs — enableUserScoping(), enableTenantScoping(), showAllRecords() — and how you combine them defines your security model. The patterns that hold up in production:

// User panel: people only see what they deleted
RevivePlugin::make()
    ->enableUserScoping(true)
    ->enableTenantScoping(false)
    ->navigationLabel('My Deleted Items');

// Tenant panel: whole team sees all team deletions
RevivePlugin::make()
    ->enableTenantScoping()
    ->enableUserScoping(false);

// Admin panel: everything, gated behind authorization
RevivePlugin::make()
    ->showAllRecords()
    ->authorize(fn () => auth()->user()->isAdmin());

Note that the same plugin can be registered in multiple panels with different configuration. That is the intended architecture — one panel provider per audience, each with its own scoping and labels — rather than one config trying to serve everyone.

Treat authorize() as mandatory on any showAllRecords() panel. It accepts a boolean or a Closure, so ->authorize(fn () => auth()->user()->hasRole('admin')) is evaluated per-request. Skipping this on an admin panel exposes every tenant's deleted data to anyone who can reach the route.

Model scoping via models() and modelsNamespace() are different tools. Use models([Post::class, Comment::class]) when you want a curated subset visible in a specific panel (e.g. a tenant panel that shouldn't surface User deletions). Use modelsNamespace([...]) when your models live outside App\Models — DDD, modular monoliths, Modules\*\Models. Namespaces resolve through Composer's PSR-4 map, so no path config is needed:

RevivePlugin::make()->modelsNamespace([
    'App\\Models',
    'App\\Domain\\Blog\\Models',
    'Modules\\Shop\\Models',
]);

Override attribution on the model, not the plugin. The default getDeletedByUser() and getTenantId() are sensible, but real schemas rarely match the defaults. Overriding them on the model is the cleanest extension point:

public function getTenantId()
{
    return $this->organization_id
        ?? filament()->getTenant()?->id;
}

This is also how you handle non-Filament tenancy — a team_id or workspace->tenant_id chain lives in one method per model rather than in plugin config.

Embed the Livewire component when a full page is too much. The plugin's page isn't the only surface; you can drop the table into a custom page, a dashboard widget, or a modal:

@livewire(\Promethys\Revive\Tables\RecycleBin::class, [
    'user' => auth()->user(),
    'enableUserScoping' => true,
    'enableTenantScoping' => false,
])

Passing the scoping parameters at the component level means an embedded instance can be narrower than the panel-wide default.

Snapshots are your audit trail. Revive stores model data at deletion time. Day to day, that means a restored record is the record as it was when deleted — not as it was after any later edits. If you need "what did this look like when it was removed" for compliance or support tickets, the snapshot is already there; don't build a parallel audit table.

CLI discovery belongs in your deploy/backfill runbook. After a data import, a schema migration that soft-deletes rows in bulk, or a V1→V2 upgrade, run discovery scoped to the right model or panel:

php artisan revive:discover-soft-deleted --model=Product --dry-run
php artisan revive:discover-soft-deleted --panel=admin --with-scope

The --dry-run first, targeted-model second pattern is the one that saves you from a 200k-row scan you didn't intend.

Gotchas and Tips

Recyclable without SoftDeletes throws. This is a hard exception, not a silent no-op. It's actually helpful — it fails loudly at the right moment — but be aware when adding the trait to a model that gets SoftDeletes from a base class or another trait; the check may not see it and you'll get a confusing boot failure.

modelsNamespace() with an array replaces App\Models entirely. This is the most likely "why did my models disappear from the bin" bug. If you pass ['Modules\\Shop\\Models'] and your User model is in App\Models, it's no longer scanned. Always include 'App\\Models' explicitly if you still want it:

->modelsNamespace(['App\\Models', 'Modules\\Shop\\Models'])

Unresolvable namespaces are skipped and logged rather than fatal, so a typo in a namespace won't crash the panel — it will just quietly scan nothing. Check the log if a namespace "doesn't work."

Duplicate migrations on re-running install. revive:install is idempotent only with spatie/laravel-package-tools >= 1.93.1. On older versions, re-running the command republishes migrations with fresh timestamps and you get duplicate-table failures. If you hit this, composer update spatie/laravel-package-tools and clean up the duplicate migration files by hand before re-running.

Match the major version to your Filament version, and be deliberate about it. The lines are strict: V1 → Filament v3, V2 → Filament v4, V3 → Filament v5. A Filament upgrade means a Revive major bump too:

composer require promethys/revive:^3.0   # Filament v5

V2→V3 has no schema changes — just a constraint bump and cache clear. V1→V2 does have schema changes, so publish and run migrations, then run revive:discover-soft-deleted --with-scope to backfill the new user/tenant columns. Plan that discovery run as part of the upgrade, not after it.

Third-party models need a wrapper, not a namespace hack. You can't just point modelsNamespace() at a vendor package's namespace cleanly, and you shouldn't try. Subclass in one of your scanned namespaces:

namespace App\Models;

use Promethys\Revive\Concerns\Recyclable;
use Vendor\Package\Models\Foo as BaseFoo;

class Foo extends BaseFoo
{
    use \Illuminate\Database\Eloquent\SoftDeletes;
    use Recyclable;
}

This also gives you a natural place to override getTenantId() for that vendor model.

The discover command runs against the default panel unless you say otherwise. There's no "current panel" in CLI context, so modelsNamespace() config on a non-default panel is invisible to the command. If your recyclable models live outside App\Models and discovery keeps reporting "no soft-deleted records found," pass --panel= explicitly.

--dry-run before every non-trivial discovery. The command scans every recyclable model; on a large database with broad namespaces that's not free. Scope with --model first, confirm counts, then run wide.

Debugging "the page is empty." Work the checklist in order: (1) is the trait on the model and does the model actually use SoftDeletes? (2) is the model's namespace covered by modelsNamespace()? (3) is scoping hiding it — try ->withoutScoping() temporarily to confirm? (4) is the record tracked at all — run revive:discover-soft-deleted --model=YourModel --dry-run. Steps 3 and 4 catch the vast majority of reports.

withoutScoping() is a debugging switch, not a production setting. It reproduces V1's "everyone sees everything" behavior. Useful for isolating whether an empty bin is a scoping problem or a discovery problem; dangerous if it ships.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator