Laravel Cooldown - це пакет від Mahedi Zaman Zaber, який дозволяє встановлювати часове блокування на іменовані дії. Якщо RateLimiter Laravel підраховує кількість запитів до ендпоінту в межах часового вікна, то Cooldown відстежує, чи конкретна дія для конкретного власника все ще перебуває в періоді очікування, і може зберігати цей стан у базі даних, а не лише в кеші.
Основні можливості
Пакет пропонує широкий набір функцій для роботи з часовими блокуваннями:
- Дії з прив'язкою до власника через
Cooldown::for('resend_verification', $user), де власником може бути будь-яка модель, IP-адреса у вигляді рядка або відсутність власника для глобального блокування
- Трейт
HasCooldowns, який додає метод $user->cooldown('change_avatar') до будь-якої Eloquent-моделі
- Middleware для маршрутів у форматі
cooldown:action,duration з опціональним аргументом драйвера
- Атомарне виконання через
block(), який встановлює блокування навколо callback-функції та активує cooldown лише у разі успіху
- Метод
enforce() для викидання CooldownActiveException, яке рендериться як HTTP 429
- Незмінний об'єкт
CooldownInfo з методами remainingSeconds() та remainingForHumans()
- Бекенди cache або database, які можна перемикати для кожного виклику через
using()
- Очищення застарілих записів бази даних через команду Laravel
model:prune
Fluent API
Cooldown складається з іменованої дії, опціонального власника та тривалості. Після визначення можна встановлювати, перевіряти або скидати блокування:
use ZaberDev\Cooldown\Facades\Cooldown;
Cooldown::for('rebuild_search_index')->for(600);
Cooldown::for('resend_verification', $user)->for(900);
Cooldown::for('daily_checkin', $user)->until(now()->endOfDay());
Коли cooldown активний, метод info() повертає об'єкт з деталями тривалості:
if (Cooldown::for('resend_verification', $user)->active()) {
$info = Cooldown::for('resend_verification', $user)->info();
echo "Please wait " . $info->remainingForHumans() . " before requesting another email.";
echo "Seconds remaining: " . $info->remainingSeconds();
}
Якщо потрібно зупинити запит замість розгалуження логіки, enforce() викидає виключення, коли дія все ще заблокована:
Cooldown::for('request_password_reset', $user)->enforce();
Атомарне блокування
Cooldown створений саме для ситуацій на кшталт відправки OTP або списання коштів з картки, коли два одночасні запити можуть пройти перевірку до того, як будь-який з них встановить блокування. Метод block() вирішує цю проблему, отримуючи lock, виконуючи callback і застосовуючи cooldown лише у разі успішного виконання:
Cooldown::for('send_login_code', $user)->block(function () use ($smsClient, $user) {
$smsClient->sendCode($user->phone);
}, duration: 90);
Якщо потрібно управляти блокуванням власноруч, доступні методи acquireLock() та releaseLock().
Cooldown на Eloquent-моделях
Додайте трейт HasCooldowns, і той самийBuilder буде доступний на екземплярі моделі з автоматичною прив'язкою до запису:
use ZaberDev\Cooldown\HasCooldowns;
class User extends Authenticatable
{
use HasCooldowns;
}
Використання:
$user->cooldown('change_avatar')->for(300);
if ($user->cooldown('change_avatar')->active()) {
return response()->json(['message' => 'You can change your avatar again shortly.'], 429);
}
$user->cooldown('change_avatar')->reset();
При використанні database-бекенду записи є поліморфними, тому cooldown моделі можна запитувати як будь-яке інше відношення:
$activeCooldowns = $user->cooldowns()->where('expires_at', '>', now())->get();
$user->cooldowns()->delete();
Middleware для маршрутів
Middleware cooldown приймає назву дії, тривалість у секундах та опціональний драйвер:
Route::post('/feedback', [FeedbackController::class, 'store'])
->middleware('cooldown:submit_feedback,120');
Route::post('/invoices/export', [InvoiceController::class, 'export'])
->middleware('cooldown:invoice_export,600,database');
Бекенди для зберігання
За замовчуванням використовується драйвер cache, що базується на налаштованому сховищі, тому працюють і Redis, і Memcached. Драйвер database натомість записує дані в таблицю cooldowns, що корисно, коли блокування пов'язане з критичними операціями на кшталт біллінгу і не може бути видалене під час очищення кешу. Драйвер можна вибирати для кожного виклику:
Cooldown::for('poll_job_status', $ip)->using('cache')->for(20);
Cooldown::for('renew_subscription', $user)->using('database')->for(86400);
Якщо потрібне інше сховище, Cooldown::extend() дозволяє зареєструвати власний драйвер із service provider. Застарілі записи бази даних можна очищати за розкладом командою Laravel model:prune.
Встановлення
Пакет вимагає PHP 8.2 і підтримує Laravel 11, 12 та 13:
composer require zaber-dev/laravel-cooldown
php artisan vendor:publish --provider="ZaberDev\Cooldown\CooldownServiceProvider"
php artisan migrate
Файл config/cooldown.php встановлює драйвер за замовчуванням (COOLDOWN_DRIVER), сховище кешу та префікс ключів, назву таблиці бази даних та чи відправляються події.
Інструкції з встановлення та повну документацію можна знайти на GitHub.