zackaj/laravel-debounce
Debounce Laravel jobs, notifications, and (Laravel 11+) Artisan commands to prevent spamming users and queues. Uses unique job locks + cache to delay execution until activity stops. Tracks each occurrence with request metadata (IP, user) and provides reporting.
Installation:
composer require zackaj/laravel-debounce
Publish config (optional):
php artisan vendor:publish --tag=laravel-debounce-config
First Use Case: Debounce a notification in a controller:
use Zackaj\LaravelDebounce\Facades\Debounce;
Debounce::notification(
notifiables: $users,
notification: new FileUploaded($file),
delay: 5, // seconds
uniqueKey: auth()->user()->id
);
Zackaj\LaravelDebounce\Facades\Debounce for quick integration.config/debounce.php for enabling/disabling globally.DebounceJob, DebounceNotification, or DebounceCommand for tracking.Debouncing Jobs:
// Using facade
Debounce::job(new ProcessPayment($order), delay: 10, uniqueKey: $order->id);
// Using job instance (extend DebounceJob)
(new ProcessPayment($order))->debounce(delay: 10, uniqueKey: $order->id);
Debouncing Notifications:
// Using facade
Debounce::notification(
notifiables: $users,
notification: new WelcomeNotification(),
delay: 30,
uniqueKey: 'welcome_'.$user->id
);
// Using notification instance (extend DebounceNotification)
(new WelcomeNotification())->debounce($users, delay: 30, uniqueKey: 'welcome_'.$user->id);
Debouncing Artisan Commands (Laravel ≥11):
// Using facade
Debounce::command(
command: 'report:generate',
delay: 60,
uniqueKey: 'daily_report',
parameters: ['--format' => 'pdf']
);
// CLI debounce (Laravel ≥11)
php artisan debounce:command 60 daily_report report:generate --format=pdf
sync: false (default) to dispatch jobs to the queue.$report = $this->getReport();
$report->occurrences->first()->ip; // Track IP
$report->occurrences->first()->user; // Track authenticated user
getLastActivityTimestamp() for dynamic debouncing:
public function getLastActivityTimestamp(): ?Carbon
{
return $this->model->updated_at;
}
public function before(): void { /* Pre-execution logic */ }
public function after(): void { /* Post-execution logic */ }
public static function before(): void { /* CLI pre-execution */ }
public static function after(): void { /* CLI post-execution */ }
Cache Dependency:
php artisan cache:clear) resets all debounced tasks.Laravel Version Quirks:
Hook Timing:
after() hooks may fire before queue dispatch if sync: false is set for queued jobs/notifications.after() for post-queue logic (e.g., logging) or move critical logic to handle()/toArray().Unique Key Collisions:
uniqueKey values (e.g., static strings) may cause unintended merging of unrelated tasks.user_id, order_id).Telescope Integration: Monitor debounced tasks in Laravel Telescope’s Queues tab.
// Enable Telescope monitoring
$this->getReport()->occurrences->each(function ($occurrence) {
\Illuminate\Support\Facades\Log::info('Debounce event', $occurrence->toArray());
});
Testing: Disable debouncing globally in tests:
config(['debounce.enabled' => false]);
Or per-test:
$this->app->singleton('debounce', fn() => new \Zackaj\LaravelDebounce\DebounceManager(false));
Custom Drivers:
Extend Zackaj\LaravelDebounce\Contracts\DebounceDriver to support non-cache backends (e.g., database).
Report Enhancements:
Override getReport() in base classes to add custom metadata:
public function getReport(): DebounceReport
{
return parent::getReport()->withMetadata(['custom_field' => $this->value]);
}
Dynamic Delays:
Implement logic in getDelay() to adjust delays per context:
public function getDelay(): int
{
return $this->isUrgent() ? 1 : 30;
}
cache:lock TTLs to avoid timeouts during spikes.now()->addMinutes(1) for delays >60s.How can I help you explore Laravel packages today?