Laravel Cooldown — це пакет від Mahedi Zaman Zaber для встановлення часових обмежень на певні дії: наприклад, заборона повторного надсилання OTP протягом 120 секунд, ліміт на запити для скидання пароля або пауза між спробами оплати. На відміну від стандартного RateLimiter, який рахує кількість запитів до ендпоїнту, 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()для отримання інформації про час, що залишився. - Підтримка кешу та бази даних з можливістю перемикання драйвера «на льоту» через
using(). - Очищення (Pruning) застарілих записів у базі даних за допомогою стандартної команди 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 "Будь ласка, зачекайте " . $info->remainingForHumans() . " перед повторним запитом.";
echo "Залишилося секунд: " . $info->remainingSeconds();
}
Якщо ви хочете одразу зупинити виконання запиту, використовуйте enforce():
Cooldown::for('request_password_reset', $user)->enforce();
# Атомарне блокування
Cooldowns розв'язують проблему станів гонитви (race conditions). Наприклад, під час надсилання OTP або оплати карткою два одночасні запити можуть пройти перевірку до того, як перший встановить обмеження. Метод block() запобігає цьому: він створює блокування, виконує код і встановлює cooldown лише за умови успіху:
Cooldown::for('send_login_code', $user)->block(function () use ($smsClient, $user) {
$smsClient->sendCode($user->phone);
}, duration: 90);
Для ручного керування блокуванням доступні методи acquireLock() та releaseLock().
# Cooldowns у моделях Eloquent
Додайте трейт HasCooldowns до моделі, щоб використовувати функціонал безпосередньо з її екземпляра:
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' => 'Ви зможете змінити аватар трохи пізніше.'], 429);
}
$user->cooldown('change_avatar')->reset();
При використанні бази даних записи є поліморфними, тому обмеження моделі можна отримати як звичайний зв'язок:
$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.
# Встановлення
Пакет вимагає 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.