کولداون: قفل زمانی روی هر اکشن تو لاراول
خلاصهٔ کاملتر
تو مقالهٔ لاراولنیوز پکیج Laravel Cooldown معرفی شده؛ ساختهٔ Mahedi Zaman Zaber که کارش گذاشتن یه قفل زماندار روی یه اکشن نامداره. نمونههای کاربردش دقیقاً همون چیزاییه که هر پروژهای باهاش درگیره: کد تأیید دوم تا ۱۲۰ ثانیه ارسال نشه، درخواست ریست رمز عبور دقیقهای یک بار باشه، یا تلاش دوبارهٔ پرداخت تا بسته شدن بازهٔ زمانی امکانپذیر نباشه.
تفاوتش با RateLimiter خود لاراول همینجاست: RateLimiter میشماره که چند درخواست تو یه بازه به یه اندپوینت خورده، ولی Cooldown دنبال میکنه که آیا یه اکشن مشخص برای یه مالک مشخص هنوز تو دورهٔ انتظارشه یا نه — و میتونه این وضعیت رو بهجای کش، تو دیتابیس نگه داره.
API روون پکیج از سه چیز ساخته شده: اسم اکشن، مالک اختیاری، و مدت زمان. مالک میتونه هر مدلی باشه، یه رشتهٔ IP، یا اصلاً هیچی برای یه قفل سراسری. بعد از نامگذاری میشه ستش کرد، چکش کرد یا پاکش کرد؛ until() هم بهجای ثانیه یه لحظهٔ مشخص مثل پایان روز رو میگیره.
Cooldown::for('resend_verification', $user)->for(900);
if (Cooldown::for('resend_verification', $user)->active()) {
$info = Cooldown::for('resend_verification', $user)->info();
echo "Please wait " . $info->remainingForHumans();
}شیء CooldownInfo که برگردونده میشه immutable هست و remainingSeconds() و remainingForHumans() میده. اگه ترجیح میدی بهجای شرط گذاشتن، درخواست رو کلاً متوقف کنی، enforce() یه CooldownActiveException پرت میکنه که بهشکل HTTP 429 رندر میشه.
مهمترین بخش از نظر مقاله، اجرای اتمیکه. مشکل واقعی جایی پیش میآد که دو درخواست همزمان هر دو چک رو رد کنن قبل از اینکه یکیشون قفل رو بذاره — مثلاً ارسال OTP یا کشیدن پول از کارت. متد block() قفل رو میگیره، کالبک رو اجرا میکنه و کولداون رو فقط در صورت موفقیت اعمال میکنه:
Cooldown::for('send_login_code', $user)->block(function () use ($smsClient, $user) {
$smsClient->sendCode($user->phone);
}, duration: 90);روی مدلهای Eloquent هم با تِرِیت HasCooldowns همون بیلدر به شکل $user->cooldown('change_avatar') در دسترسه و به همون رکورد اسکوپ میشه. روی درایور دیتابیس رکوردها polymorphic ذخیره میشن، پس $user->cooldowns() مثل هر رابطهٔ دیگهای قابل کوئری گرفتنه. یه میدلور روت هم هست با فرم cooldown:action,duration و آرگومان اختیاری درایور.
درایور پیشفرض cache هست و با Redis و Memcached کار میکنه؛ درایور database روی جدول cooldowns مینویسه و مناسب جاییه که قفل به چیزی مثل صورتحساب گره خورده و نباید با یه cache flush بپره. انتخاب درایور با using() حتی برای هر فراخوانی جداگانه ممکنه، Cooldown::extend() درایور سفارشی ثبت میکنه و ردیفهای منقضیشده با دستور model:prune لاراول پاک میشن. پکیج به PHP 8.2 نیاز داره و از لاراول ۱۱، ۱۲ و ۱۳ پشتیبانی میکنه.
نکات کلیدی:
- Cooldown بهجای شمردن درخواستها، دورهٔ انتظار یه اکشن مشخص برای یه مالک مشخص رو دنبال میکنه
- مالک میتونه هر مدل Eloquent، یه IP یا هیچی (قفل سراسری) باشه
- block() با گرفتن لاک، مشکل رقابت دو درخواست همزمان رو حل میکنه و فقط بعد از موفقیت قفل میذاره
- enforce() استثنا پرت میکنه که به HTTP 429 تبدیل میشه؛ CooldownInfo زمان باقیمونده رو میده
- دو درایور cache و database، قابل انتخاب در هر فراخوانی با using() و پاکسازی با model:prune
- نیازمند PHP 8.2 و سازگار با لاراول ۱۱، ۱۲ و ۱۳




