laravel-sweep maintained by anjan-talukdar
Laravel Sweep 🧹
Sweep is an open-source, multi-database, storage-agnostic data purging and sequence reset library for Laravel applications.
It empowers developers, staging environments, QA teams, and CI/CD pipelines to selectively or completely sweep away user, tenant, and transactional records—along with associated physical storage files and S3 media—while strictly safeguarding master reference tables and system assets.
🌟 Why Sweep?
- Multi-Database Native: Built-in specialized drivers for MySQL, PostgreSQL, and SQLite. Handles foreign key constraints and resets table auto-increment sequences (
AUTO_INCREMENT = 1,pg_get_serial_sequencewithsetval, orsqlite_sequence). - Spatie Media Library & S3 Agnostic: Automatically discovers eligible media models and cleanly deletes media files from Amazon S3, Local, R2, or MinIO via Spatie Media Library, with strict protection for catalog/system templates.
- Master Table Whitelist: Hardcoded and configurable protection ensuring tables like
countries,states,migrations,roles,permissions, and reference catalogs are never accidentally dropped or truncated. - Granular Domain Groups: Organize tables child-first into logical business domains (e.g.,
chat,billing,events,users). Theallgroup is automatically synthesized. - Rich Dry-Run Previews: Full
--dry-runinspection table reporting exact row counts and media assets before touching any data. - Headless & Interactive: Works interactively via Laravel Prompts
multiselect(), or non-interactively in CI/CD using--force. - Programmatic API: Use the
Sweepfacade directly inside PHPUnit / Pest tests, seeders, or admin dashboards.
📦 Installation
Install Sweep via Composer:
composer require anjan-talukdar/laravel-sweep
Publish the configuration file:
php artisan vendor:publish --tag=sweep-config
This will create config/sweep.php in your application.
⚙️ Configuration
config/sweep.php lets you define your domain groups, master safeguards, and storage cleaner rules:
return [
/*
|--------------------------------------------------------------------------
| Default Command Name
|--------------------------------------------------------------------------
| The Artisan command registered by Sweep. Default is 'sweep'.
*/
'command_name' => env('SWEEP_COMMAND', 'sweep'),
/*
|--------------------------------------------------------------------------
| Universal Master & Reference Tables (NEVER Swept)
|--------------------------------------------------------------------------
| These tables can NEVER be truncated or deleted, regardless of input.
*/
'protected_tables' => [
'migrations',
'countries', 'states', 'cities',
'roles', 'permissions', 'role_has_permissions',
'system_settings', 'plans', 'plan_features',
],
/*
|--------------------------------------------------------------------------
| Protected Spatie Media Models
|--------------------------------------------------------------------------
| Media records for these model classes will NEVER be deleted from S3/disk.
*/
'protected_media_models' => [
'App\Models\SystemTemplate',
'App\Models\BrandAsset',
],
/*
|--------------------------------------------------------------------------
| Functional Domain Groups
|--------------------------------------------------------------------------
| Map tables child-first to satisfy foreign keys.
*/
'groups' => [
'chat' => [
'label' => 'Chat Messages & Conversations',
'tables' => [
'messages',
'conversations',
],
],
'billing' => [
'label' => 'Subscriptions, Invoices & Usages',
'tables' => [
'invoice_items',
'invoices',
'payments',
'subscriptions',
],
],
'users' => [
'label' => 'Users, Sessions & Tokens',
'tables' => [
'sessions',
'password_reset_tokens',
'model_has_roles',
'users',
],
'media_models' => [
\App\Models\User::class, // Purges user avatars on S3/local
],
],
],
/*
|--------------------------------------------------------------------------
| Physical Storage Paths (Local / S3 / R2)
|--------------------------------------------------------------------------
*/
'storage_paths' => [
'temp' => [
['disk' => 'local', 'path' => 'temp-exports'],
['disk' => 's3', 'path' => 'temporary-uploads'],
],
],
/*
|--------------------------------------------------------------------------
| Post-Purge Lifecycle Hooks
|--------------------------------------------------------------------------
*/
'after_cleaning' => [
// App\Actions\ResetTenantCaches::class,
],
];
🚀 CLI Usage
Interactive Selection
Run without arguments to select domain groups via an interactive multi-select menu:
php artisan sweep
Specific Domain Groups
Target one or multiple groups (comma-separated):
php artisan sweep --group=chat,billing
Dry-Run Simulation (Preview Without Modifying)
Simulate cleanup and view an inspection table with row counts:
php artisan sweep --group=all --dry-run
Sample output:
+---------+--------------------+-------------------+-------------------------------+
| Group | Target Table | Records to Purge | Planned Action |
+---------+--------------------+-------------------+-------------------------------+
| chat | messages | 14,250 | Delete & Reset Sequence -> 1 |
| chat | conversations | 820 | Delete & Reset Sequence -> 1 |
| users | sessions | 3,100 | Delete & Reset Sequence -> 1 |
| users | users | 412 | Delete & Reset Sequence -> 1 |
+---------+--------------------+-------------------+-------------------------------+
Total records eligible for purge: 18,582
Eligible Spatie Media records (and physical disk/S3 files): 412
[DRY RUN COMPLETE] Zero database rows or disk files were modified.
Keep Storage (Preserve S3 / Local Files)
Purge database records and reset primary key sequences, but leave S3/local media intact:
php artisan sweep --group=all --keep-storage
Headless & CI/CD Execution
Bypass interactive prompts (required when running in production):
php artisan sweep --group=all --force
💻 Programmatic API (Facade)
You can call Sweep directly from PHP inside test suites, seeders, or controllers:
use AnjanTalukdar\Sweep\Facades\Sweep;
// Run sweep for specific groups
Sweep::run(groups: ['chat', 'billing'], keepStorage: false);
// Run dry-run simulation and inspect results
$report = Sweep::dryRun(groups: ['all']);
echo "Total records eligible: " . $report->totalRecords;
echo "Eligible Spatie media files: " . $report->eligibleMediaCount;
foreach ($report->tables as $item) {
echo "{$item['table']}: {$item['count']} rows\n";
}
🗄️ Multi-Database Engine Handling
| Database Engine | Foreign Key Handling | Sequence / Auto-Increment Reset |
|---|---|---|
| PostgreSQL | Transactional child-first delete; or TRUNCATE TABLE "..." RESTART IDENTITY CASCADE on full wipe. |
Resolves sequence via pg_get_serial_sequence(?, 'id') and executes SELECT setval(seq, 1, false). |
| MySQL / MariaDB | Sets SET FOREIGN_KEY_CHECKS = 0; before deletes and restores SET FOREIGN_KEY_CHECKS = 1;. |
Executes ALTER TABLE \table` AUTO_INCREMENT = 1;`. |
| SQLite | Sets PRAGMA foreign_keys = OFF; before deletes and restores PRAGMA foreign_keys = ON;. |
Executes DELETE FROM sqlite_sequence WHERE name = ?;. |
🔔 Events
Sweep dispatches framework events throughout its lifecycle:
AnjanTalukdar\Sweep\Events\SweepStarting: Dispatched before operations start (includes$selectedGroupsand$dryRunflag).AnjanTalukdar\Sweep\Events\TableSwept: Dispatched after each database table is purged.AnjanTalukdar\Sweep\Events\SweepCompleted: Dispatched when the sweep operation finishes.
🧪 Testing
composer test
📄 License
Laravel Sweep is open-sourced software licensed under the MIT license.