21 β Events, Listeners va Scheduling¶
β¬ οΈ Oldingi: 20 β Queues va Jobs Β· π README Β· Keyingi: 22 β Testing (Pest va PHPUnit) β‘οΈ
Bu bobda: "biror narsa bo'lganda avtomatik reaksiya" va "vaqt bo'yicha avtomatik ish" muammosini yechamiz. Avval Event (hodisa) va Listener (tinglovchi) bilan kodni ajratishni (decoupling) o'rganamiz: bitta hodisa β bir nechta mustaqil reaksiya,
make:event/make:listener,event()dispatch va avtomatik discovery. Keyin Model Observer (make:observer) bilan modelning hayot siklidagi (created/updated/deleting...) hooklarni ushlaymiz. So'ng Task Scheduling (Laravel 11+ uslubi:routes/console.phpdaSchedule::command(...)->daily()) β serverda BITTA cron yozuvi bilan hamma rejalashtirilgan vazifani boshqarish,schedule:list/schedule:run/schedule:work. Yo'l-yo'lakaymake:commandbilan o'z artisan buyrug'imizni va listenerni navbatga (queue) qo'yishni ko'rib chiqamiz.
Muammo¶
Foydalanuvchi ro'yxatdan o'tdi. Endi nima bo'lishi kerak? O'ylab ko'raylik:
- Unga "Xush kelibsiz" emaili yuborilsin.
- Logga "yangi user qo'shildi" deb yozilsin.
- Adminga Telegram/Slack xabar borsin.
- Statistika
+1bo'lsin.
PHP biladigan dasturchi sifatida birinchi xayolingizga keladigan yechim β hammasini register() controlleri ichiga ketma-ket yozish:
public function register(Request $request)
{
$user = User::create($request->validated());
Mail::to($user)->send(new WelcomeMail($user)); // 1
Log::info('Yangi user: ' . $user->email); // 2
Http::post('https://api.telegram.org/...', [...]); // 3
Statistika::increment('users'); // 4
return redirect('/dashboard');
}
Ishlaydi. Lekin muammolar ketma-ket yog'ila boshlaydi:
- Controller shishadi. Ro'yxatdan o'tish β bitta gap edi (
User::create), lekin metod 4 ta begona vazifa bilan to'ldi. - Bog'liqlik (coupling). Endi
RegisterControllerMail'ni ham, Telegram'ni ham, Statistika'ni ham "biladi". Bittasi o'zgarsa, controllerga tegish kerak. - Takrorlanish. Admin user'ni qo'lda yaratadigan boshqa joy bor (
AdminUserController). U yerda ham xuddi shu 4 qatorni qaytarib yozasizmi? Birini unutsangiz β xush kelibsiz email yubormay qolasiz. - Test qiyin.
register()ni test qilmoqchisiz β endi haqiqiy email ketib qoladi.
Asl muammo bitta: "user yaratildi" degan voqea bilan "o'sha voqeaga javoban bajariladigan ishlar" bir-biriga yopishib qolgan. Aslida controller faqat shuni e'lon qilishi kerak: "user ro'yxatdan o'tdi!" β kim eshitsa, o'zi reaksiya qilsin. Mana shu β Event / Listener g'oyasi.
Ikkinchi muammo butunlay boshqacha, lekin u ham "avtomatik" haqida: hech kim tugma bosmasa ham bajariladigan ishlar bor. Har kecha soat 02:00 da hisobot, har soatda eski fayllarni tozalash, har hafta dushanba kuni adminlarga xulosa email. Buni qo'lda bajarib bo'lmaydi β vaqt kelganda o'zi ishlashi kerak. Bu β Task Scheduling.
Ikkalasini ham shu bobda yechamiz.
Event va Listener β voqea va unga javob¶
Tushunchani ikki qismga bo'lamiz:
- Event (hodisa) β "biror narsa bo'ldi" degan e'lon. Bu shunchaki ma'lumotli ob'ekt:
UserRegistered(ichida$userbor). U hech qanday ish bajarmaydi β faqat "bu voqea sodir bo'ldi" deb xabar beradi. - Listener (tinglovchi) β o'sha hodisaga javob beradigan ish: email yuborish, log yozish. Bitta event'ga bir nechta listener bog'lanishi mumkin.
Aloqa shunday: controller event(new UserRegistered($user)) deb hodisani e'lon qiladi (dispatch). Laravel o'sha event'ga ulangan barcha listenerlarni topib, har birini chaqiradi. Controller listenerlar haqida hech narsa bilmaydi β mana shu ajratish (decoupling).
π Bu β gazeta e'loni kabi. Controller "user ro'yxatdan o'tdi" deb gazetaga e'lon beradi. Gazetani kim o'qisa (listener), o'zi tegishli ishini qiladi. E'lon beruvchi o'quvchilarning ro'yxatini yuritmaydi.
Event yaratish¶
Bu app/Events/UserRegistered.php faylini yaratadi. Uni shunday to'ldiramiz β ichiga faqat ma'lumot qo'yamiz:
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, SerializesModels;
public function __construct(public User $user)
{
}
}
π‘ public User $user β PHP 8 ning constructor property promotion'i (PHP kitobida ko'rgansiz). Listenerlar event ichidagi $user ga shu orqali yetadi. Dispatchable trait UserRegistered::dispatch($user) qisqa yozuvni beradi, SerializesModels esa event navbatga tushganda modelni to'g'ri saqlaydi.
Listener yaratish¶
--event=UserRegistered bayrog'i listener'ning handle() metodiga to'g'ri tip-hint qo'yib beradi. app/Listeners/SendWelcomeEmail.php:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;
class SendWelcomeEmail
{
public function handle(UserRegistered $event): void
{
Mail::to($event->user)->send(new WelcomeMail($event->user));
}
}
E'tibor bering: handle() ga UserRegistered $event keladi β undan $event->user ni olamiz. Listener faqat bitta ish qiladi: email yuboradi. Ikkinchi listener (log) β alohida fayl, alohida mas'uliyat:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Log;
class LogRegistration
{
public function handle(UserRegistered $event): void
{
Log::info('Yangi user royxatdan otdi: ' . $event->user->email);
}
}
Hodisani e'lon qilish (dispatch)¶
Endi controller jonsiz qoladi β faqat user yaratadi va voqeani e'lon qiladi:
public function register(Request $request)
{
$user = User::create($request->validated());
UserRegistered::dispatch($user); // <-- yagona qator
return redirect('/dashboard');
}
UserRegistered::dispatch($user) ishlatdik (Dispatchable trait shuni beradi). Bir xil ma'noli ikkinchi yo'l β global event() yordamchisi:
Ikkalasi bir xil. Endi controller email, log, Telegram haqida hech narsa bilmaydi. Yangi reaksiya kerakmi? Yangi listener yozasiz β controllerga umuman tegmaysiz. Mana shu ochiq/yopiq tamoyilning (open/closed) amaliy ko'rinishi.
β Controller faqat o'z ishini qiladi: user yaratish + "bo'ldi!" deyish. β Controllerni yana 4 ta begona vazifa bilan to'ldirish.
Laravel listenerni qanday topadi? (avtomatik discovery)¶
Eski Laravel'da har bir event -> listener bog'lanishini EventServiceProvider ning $listen massivida qo'lda yozish kerak edi. Laravel 11+ da bu avtomatik: Laravel app/Listeners papkasini o'zi skanerlaydi va har listenerning handle() metodidagi tip-hint (UserRegistered $event) ga qarab bog'lanishni o'zi topadi.
Demak, yuqoridagi SendWelcomeEmail va LogRegistration handle(UserRegistered $event) deganligi uchun, ikkalasi ham avtomatik ravishda UserRegistered ga ulanadi. Qo'lda ro'yxatga olish shart emas.
π‘ Bog'lanishlar to'g'ri topilganini ko'rishni xohlasangiz:
Bu har bir event va unga ulangan listenerlar ro'yxatini chiqaradi β discovery ishlaganini tasdiqlaydi.
π Discovery faqat tip-hintga qaraydi. handle(UserRegistered $event) da tip-hintni unutib handle($event) deb yozsangiz, Laravel qaysi event'ga ulashni bilmaydi va listener jim qoladi β xato ham bermaydi. Listener "ishlamayapti" muammosining eng tez-tez uchraydigan sababi shu.
Queued Listener β sekin ishni navbatga qo'yish¶
Email yuborish β sekin ish (SMTP serveriga ulanish kerak). Hozirgi holatda foydalanuvchi register() tugmasini bosgach, javob kelishidan oldin email yuborilib bo'lguncha kutadi. 20-bobda buni navbat (queue) bilan yechgan edik. Listenerni navbatga qo'yish juda oson: listener ShouldQueue interfeysini implement qilsa kifoya:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
Mail::to($event->user)->send(new WelcomeMail($event->user));
}
}
Bitta implements ShouldQueue qo'shildi β boshqa hech narsa o'zgarmadi. Endi UserRegistered::dispatch() chaqirilganda, Laravel bu listenerni darhol bajarmaydi β uni navbatga qo'yadi, va queue:work worker'i uni fonda ishlatadi. Foydalanuvchi javobni kutmaydi.
π Muhim nozik nuqta: bitta event'ning listenerlaridan biri queued (SendWelcomeEmail), boshqasi oddiy (LogRegistration) bo'lishi mumkin. Log darhol yoziladi, email esa fonda ketadi β har biri o'ziga mos.
π‘ Listener queue'da xato berib, qayta urinish (retry) kerak bo'lsa, $tries va backoff xossalarini Job'dagi kabi qo'shasiz (20-bobni eslang):
class SendWelcomeEmail implements ShouldQueue
{
public int $tries = 3;
public int $backoff = 10; // har urinish orasida 10 soniya
// ...
}
Model Observer β modelning hayot sikliga ulanish¶
Event/Listener "men o'zim dispatch qilganimda" ishlaydi. Lekin ko'pincha reaksiya bevosita modelga bog'liq: "har qachon Post yaratilsa", "har qachon User o'chirilsa". Bunda har bir create()/update() joyida qo'lda event tashlash zerikarli va unutiluvchan.
Yechim β Observer (kuzatuvchi). Eloquent modeli o'z hayoti davomida ichki hodisalar chiqaradi: creating, created, updating, updated, deleting, deleted va boshqalar. Observer β shu hodisalarni ushlovchi maxsus sinf.
π -ing va -ed farqi muhim:
- creating β bazaga yozishdan oldin (qiymatni o'zgartirib ulgurasiz; false qaytarsangiz amalni bekor qiladi).
- created β bazaga yozilib bo'lgandan keyin (endi $model->id mavjud).
Observer yaratish¶
--model=Post bayrog'i observerni Post ga moslab, asosiy metod qoliplarini yozib beradi. app/Observers/PostObserver.php:
<?php
namespace App\Observers;
use App\Models\Post;
use Illuminate\Support\Str;
class PostObserver
{
public function creating(Post $post): void
{
// Bazaga yozishdan OLDIN: slug yoq bolsa, sarlavhadan yasaymiz
if (empty($post->slug)) {
$post->slug = Str::slug($post->title);
}
}
public function created(Post $post): void
{
// Yozilgandan KEYIN: endi $post->id bor
logger("Yangi post yaratildi, id: {$post->id}");
}
public function updated(Post $post): void
{
logger("Post #{$post->id} yangilandi");
}
public function deleting(Post $post): void
{
// O'chirishdan OLDIN: bog'liq fayllarni tozalaymiz
logger("Post #{$post->id} ochirilmoqda");
}
}
Observerni ro'yxatga olish¶
Laravel 11+ da eng toza yo'l β model ustiga #[ObservedBy] atributini qo'yish:
<?php
namespace App\Models;
use App\Observers\PostObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;
#[ObservedBy([PostObserver::class])]
class Post extends Model
{
protected $fillable = ['title', 'slug', 'body'];
}
Bo'ldi. Endi loyihaning istalgan joyida Post::create([...]) chaqirilsa β controllerda, Tinker'da, seederda, testda β observer o'zi ishga tushadi. Slug avtomatik yasaladi, log yoziladi. Hech bir controllerga tegmadik.
π‘ Atribut yoqmasa, muqobil yo'l β AppServiceProvider ning boot() metodida ro'yxatga olish:
π Tuzoq: Post::query()->update([...]) yoki Post::insert([...]) kabi ommaviy (mass) so'rovlar observerni ishga TUSHIRMAYDI β chunki ular modelni yuklamasdan to'g'ridan-to'g'ri SQL bajaradi. Observer faqat model instansi orqali ($post->save(), Post::create(), $post->delete()) yuradi. Bu Eloquent event'larining tabiati β yodda tuting.
Observer vs Event/Listener β qaysi biri?¶
| Holat | Tanlov |
|---|---|
Reaksiya bevosita model hayotiga bog'liq (har create/update/delete) |
Observer |
| Bitta voqea, lekin modelga bevosita bog'liq emas (to'lov o'tdi, eksport tugadi) | Event/Listener |
| Modelni har yaratilganda 1 ta ish (masalan slug) | Observer |
| Bitta voqeaga ko'p mustaqil reaksiya, ulardan ba'zilari navbatga ketishi kerak | Event/Listener |
Ikkisi raqobatchi emas β turli ehtiyoj uchun. Observer "model nimadir qildi" ga, Event/Listener "biznesda nimadir bo'ldi" ga javob beradi.
Task Scheduling β vaqt bo'yicha avtomatik ishlar¶
Endi ikkinchi muammoga o'tamiz: hech kim tugma bosmasa ham bajariladigan ishlar. Har kecha hisobot, har soat tozalash, har hafta xulosa.
An'anaviy Linux yo'li β crontab. Lekin har vazifa uchun alohida cron yozuvi qo'shsangiz, ular serverda sochilib ketadi: birini o'chirsangiz qaytarib qo'yish qiyin, jadval kodda ko'rinmaydi, git da kuzatib bo'lmaydi. Laravel buni teskari qiladi: butun jadval kodda yoziladi, serverda esa atigi bitta cron yozuvi turadi.
Jadvalni kodda yozish¶
Laravel 11+ da jadval routes/console.php faylida Schedule fasadi orqali yoziladi:
<?php
use Illuminate\Support\Facades\Schedule;
Schedule::command('report:daily')->dailyAt('02:00');
Schedule::command('cleanup:temp')->hourly();
π Bu Laravel 11+ uslubi. Eski qo'llanmalarda jadval app/Console/Kernel.php ning schedule() metodida ko'rsatiladi. Laravel 11+ da Kernel.php YO'Q β routes/console.php ishlatiladi (yoki istasangiz bootstrap/app.php ning ->withSchedule(...) qismida). Eski namunani ko'rsangiz adashmang.
Faqat artisan buyrug'ini emas, closure ni ham rejalashtirish mumkin:
Schedule::call(function () {
DB::table('sessions')->where('last_activity', '<', now()->subWeek()->getTimestamp())->delete();
})->daily();
Yoki Queue Job'ni rejalashtirish (20-bobdagi Job):
Chastota (qachon ishlaydi) metodlari¶
Laravel o'qishga oson, ingliz tilidagi kabi metodlar beradi:
Schedule::command('report:daily')->daily(); // har kuni 00:00
Schedule::command('report:daily')->dailyAt('13:30'); // har kuni 13:30
Schedule::command('cleanup:temp')->hourly(); // har soat
Schedule::command('cleanup:temp')->everyFiveMinutes(); // har 5 daqiqa
Schedule::command('backup:run')->weekly(); // har hafta (yakshanba 00:00)
Schedule::command('backup:run')->weeklyOn(1, '08:00'); // har dushanba 08:00
Schedule::command('invoice:send')->monthlyOn(1, '00:00'); // har oyning 1-sanasi
Schedule::command('report:annual')->yearly(); // yiliga bir
Murakkabroq holat uchun to'g'ridan-to'g'ri cron ifodasi yozish ham mumkin:
// Har dushanba, chorshanba, juma kunlari soat 09:15 da:
Schedule::command('report:weekday')->cron('15 9 * * 1,3,5');
π‘ Foydali qo'shimcha cheklovlar zanjirlanadi:
Schedule::command('report:daily')
->dailyAt('02:00')
->weekdays() // faqat ish kunlari (Du-Ju)
->timezone('Asia/Tashkent') // vaqt mintaqasi
->withoutOverlapping(); // oldingisi tugamagan bolsa, yangisini boshlamaslik
π withoutOverlapping() β muhim himoya. Agar vazifa 5 daqiqadan ko'p ishlasa va everyFiveMinutes() bilan rejalangan bo'lsa, ikkita nusxa bir vaqtda ishlab, bir-biriga xalaqit berishi mumkin. withoutOverlapping() buni oldini oladi.
Serverda BITTA cron yozuvi¶
Bu hammasining yuragida turgan g'oya. Yuqoridagi jadval qancha vazifadan iborat bo'lmasin, serverning crontab iga faqat bitta qator qo'shasiz:
O'qilishi: "har daqiqada (* * * * *) loyiha papkasiga kir va php artisan schedule:run ni ishga tushir". schedule:run har daqiqada uyg'onadi, routes/console.php dagi jadvalga qaraydi va aynan shu daqiqada bajarilishi kerak bo'lgan vazifalarnigina ishga tushiradi. Qolganlarini o'tkazib yuboradi.
Demak, yangi rejalashtirilgan vazifa kerak bo'lsa β routes/console.php ga bir qator qo'shasiz va git push qilasiz. Serverga, crontab'ga tegmaysiz. Mana shu β Laravel scheduler'ining asosiy yutug'i.
Jadvalni ko'rish va sinash¶
Hozir jadvalda nima borligini bilish uchun:
0 2 * * * php artisan report:daily ....... Next Due: 14 hours from now
0 * * * * php artisan cleanup:temp ........ Next Due: 23 minutes from now
Har vazifa, uning cron ifodasi va keyingi ishga tushish vaqti ko'rinadi.
Lokal mashinada (kompyuteringizda) crontab sozlamasdan jadvalni sinab ko'rish uchun:
Bu buyruq oldingi planda turadi va schedule:run ni har daqiqada o'zi chaqirib turadi β xuddi serverdagi cron kabi, lekin ishlab chiqish (development) uchun qulay. Ctrl+C bilan to'xtatasiz.
π‘ Bitta vazifani darhol sinab ko'rmoqchimisiz, vaqtini kutmasdan? Shunchaki uning buyrug'ini to'g'ridan-to'g'ri ishga tushiring: php artisan report:daily. Jadval β faqat "qachon" haqida; vazifaning o'zi oddiy artisan buyrug'i bo'lib qolaveradi.
Custom Artisan Command β o'z buyrug'ingni yasash¶
Yuqorida report:daily va cleanup:temp ni rejaladik β lekin bu buyruqlar qayerdan keldi? Ularni o'zimiz yozamiz. Bu β custom artisan command.
app/Console/Commands/SendDailyReport.php:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Order;
class SendDailyReport extends Command
{
protected $signature = 'report:daily';
protected $description = 'Har kungi sotuvlar hisobotini tayyorlaydi va yuboradi';
public function handle(): int
{
$bugun = Order::whereDate('created_at', today())->count();
$this->info("Bugun {$bugun} ta buyurtma boldi.");
// ... bu yerda hisobotni email qilib yuborasiz
return self::SUCCESS;
}
}
Ikki muhim xossa:
$signatureβ buyruqning nomi (report:daily). Aynan shu nomniSchedule::command('report:daily')da va terminaldaphp artisan report:dailydeb chaqirasiz.$descriptionβphp artisan listda ko'rinadigan qisqacha izoh.
handle() β buyruq ishi shu yerda. $this->info(), $this->error(), $this->line() bilan terminalga yozasiz. Oxirida self::SUCCESS (ya'ni 0) qaytarish β "muvaffaqiyatli tugadi" degani.
π‘ Buyruqqa argument va parametr ham berish mumkin, $signature ichida e'lon qilinadi:
{sana?}β ixtiyoriy argument (php artisan report:daily 2026-06-01).$this->argument('sana')bilan olasiz.{--email=}β qiymatli parametr (php artisan report:daily --email=admin@example.com).$this->option('email')bilan olasiz.
π make:command yaratgan buyruq avtomatik ro'yxatga olinadi β app/Console/Commands papkasidagi har bir buyruqni Laravel o'zi topadi. Qo'lda hech qayerga yozish shart emas. php artisan list da darhol ko'rinadi.
Endi doira yopildi: o'z buyrug'ingizni yozdingiz (make:command), uni routes/console.php da rejaladingiz (Schedule::command('report:daily')->dailyAt('02:00')), va serverdagi bitta cron schedule:run har kecha uni o'zi ishga tushiradi. Hech kim tugma bosmaydi β hammasi avtomatik.
Hammasini birlashtirib¶
Bu bobda ko'rganlarimiz bitta katta g'oyaga xizmat qiladi: kodni vaqt va voqeaga qarab ajratish.
- Event / Listener β "biror narsa bo'lganda" reaksiya. Controller voqeani e'lon qiladi, listenerlar mustaqil javob beradi. Bog'liqlik kamayadi, har reaksiya alohida testlanadi.
- Observer β modelning o'z hayotiga ulangan, har
create/update/deleteda avtomatik ishlovchi maxsus listener. - Queued Listener β sekin reaksiyani fonga (navbatga) suradi.
- Task Scheduling β "vaqt kelganda" ishlovchi vazifalar; jadval kodda, serverda bitta cron.
- Custom Command β o'z artisan buyrug'imiz, ham qo'lda, ham jadval orqali ishlatiladi.
Endi loyihangiz "tirik": foydalanuvchi nimadir qilganda o'zi javob beradi, vaqt o'tganda o'zi ishlaydi β sizning qo'lingizsiz.
21-bob mashqlari¶
Quyidagi mashqlarni yangi yoki mavjud Laravel loyihangizda bajaring. Har birida buyruq nomlari va metod imzolari Laravel 11+ (13) konvensiyasiga mos bo'lsin.
make:eventbilanOrderPlaced(buyurtma berildi) hodisasini yarating. Konstruktordapublic Order $orderqabul qilsin.make:listenerbilanSendOrderConfirmationlistenerini--event=OrderPlacedbayrog'i bilan yarating.handle()ichidalogger()orqali buyurtma id sini yozing.- Controllerda buyurtma yaratilgandan keyin
OrderPlaced::dispatch($order)bilan hodisani e'lon qiling.php artisan event:listda bog'lanish ko'rinishini tekshiring. - Xuddi shu
OrderPlacedhodisasiga ikkinchi listener (UpdateSalesStats) qo'shing. Bitta event'ga ikki mustaqil reaksiya ulanganinievent:listda ko'ring. SendOrderConfirmationlisteneriniimplements ShouldQueueqiling.LogRegistrationesa oddiy qolsin β birini queued, birini sinxron qiling.- Queued listener uchun
$tries = 3va$backoff = 15xossalarini qo'shing. event(new OrderPlaced($order))global yordamchisi bilan dispatch qiling vaOrderPlaced::dispatch($order)bilan bir xil natija berishini tasdiqlang.make:observerbilanPostmodeli uchunPostObserveryarating (--model=Post).- Observerning
creating()metodidaslugbo'sh bo'lsa,Str::slug($post->title)dan yasab qo'ying. created()metodidalogger()bilan yangi post id sini yozing va$post->idmavjudligini ko'rsating.#[ObservedBy([PostObserver::class])]atributi bilan observerniPostmodeliga ulang.- Tinker'da (
php artisan tinker)Post::create([...])qiling vacreating/createdishlaganini logdan tekshiring. updating()vadeleting()hooklarini qo'shib, post yangilanganda va o'chirilganda log yozilishini ko'ring.Post::query()->update([...])ommaviy so'rovi observerni ishga tushirmasligini tajriba bilan ko'ring va sababini izohlang.make:commandbilanreport:dailysignatursi va mos$descriptionga egaSendDailyReportbuyrug'ini yarating;handle()da bugungi buyurtmalar sonini chiqaring.- Shu buyruqqa
{sana?}ixtiyoriy argumentini qo'shib,$this->argument('sana')orqali aniq sana bo'yicha hisoblang. routes/console.phpdaSchedule::command('report:daily')->dailyAt('02:00')deb rejalashtiring.cleanup:tempdegan ikkinchi buyruq yasab, uni->hourly()bilan rejalashtiring; ikkalasiniphp artisan schedule:listda ko'ring.- Birinchi vazifaga
->withoutOverlapping()va->timezone('Asia/Tashkent')zanjirini qo'shing. ->cron('15 9 * * 1,3,5')ifodasi bilan dushanba/chorshanba/juma soat 09:15 ishlaydigan vazifa rejalashtiring, so'ngphp artisan schedule:workbilan jadval ishlayotganini lokal sinab ko'ring.