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 to prevent spamming users with repeated alerts:
use Zackaj\LaravelDebounce\Facades\Debounce;
Debounce::notification(
notifiables: $users,
notification: new FileUploaded($file),
delay: 5, // 5 seconds
uniqueKey: $userId
);
Zackaj\LaravelDebounce\Facades\Debounce for quick integration.config/debounce.php for enabling/disabling debouncing globally.DebounceJob, DebounceNotification, or DebounceCommand for tracking occurrences.Debouncing Jobs:
Debounce::job(
job: new ProcessPayment($order),
delay: 10,
uniqueKey: $order->id,
sync: false // Dispatch to queue
);
uniqueKey to group related jobs (e.g., user_id, order_id).sync: true for synchronous execution (bypasses queue).Debouncing Notifications:
Debounce::notification(
notifiables: $user,
notification: new WelcomeEmail(),
delay: 30,
uniqueKey: $user->email,
sendNow: false // Queue the notification
);
sendNow: false to defer sending via the queue.Debouncing Artisan Commands (Laravel ≥11):
Debounce::command(
command: 'report:generate',
delay: 60,
uniqueKey: 'daily-report',
parameters: ['--format' => 'pdf'],
toQueue: true
);
toQueue: true to execute asynchronously.php artisan debounce:command 60 daily-report report:generate --format=pdf
Extend Base Classes for advanced features:
class SendWelcomeEmail extends DebounceNotification {
public function before($notifiables) {
// Pre-send logic (e.g., log, validate)
}
public function getLastActivityTimestamp($notifiables) {
return $notifiables->lastLoginAt; // Custom timestamp logic
}
}
Report Tracking: Access occurrence history in jobs/notifications:
$report = $this->getReport();
$report->occurrences->count(); // Total attempts
$report->occurrences->first()->ip; // First request IP
Testing: Disable debouncing in tests:
config(['debounce.enabled' => false]);
Or via .env.testing:
LARAVEL_DEBOUNCE_ENABLED=false
Cache Dependence:
php artisan cache:clear) resets all debounced states.Artisan Command Limitations (Laravel <11):
DebounceCommand instead.Hook Timing:
after() hooks may run before queue dispatch if sync: false and the job implements ShouldQueue.sync: true for immediate execution or handle async logic in handle().Unique Key Collisions:
uniqueKey values (e.g., static strings) can merge unrelated debounce groups.user_id, order_id, or ip + timestamp.Telescope Integration:
telecope is installed and configured.Log Occurrences:
$this->getReport()->occurrences->each(function ($occurrence) {
Log::info('Debounce attempt', ['ip' => $occurrence->ip, 'user' => $occurrence->user]);
});
Manual Lock Inspection:
php artisan cache:table
laravel-debounce:.Custom Drivers:
Zackaj\LaravelDebounce\Contracts\DebounceDriver interface to support non-cache backends (e.g., database).Dynamic Delays:
public function getDelay(): int {
return $this->user->isPremium() ? 30 : 60; // Dynamic delay logic
}
Global Overrides:
config/debounce.php defaults:
'default_delay' => 30, // Global fallback delay
'report_ttl' => 604800, // Report retention in seconds
uniqueKey to debounce batch processes (e.g., user_id + action_type).getLastActivityTimestamp() to debounce based on domain-specific events (e.g., Message::latest()->first()?->seen_at).$delay = config('app.env') === 'production' ? 300 : 5; // 5s in dev, 5m in prod
How can I help you explore Laravel packages today?