Skip to content

Commit 28d07f0

Browse files
Add sort option for schedule:list (#45198)
* Add sort option for schedule:list Currently, in order to see what's schedule will be run, I need to run schedule:list and eyeball the list to find the relevant ones. This PR adds an option to sort the list by the next due date. Before: ``` php artisan schedule:list 0 * * * * php artisan command-one .......... Next Due: 55 minutes from now 15 * * * * php artisan command-three ........ Next Due: 10 minutes from now 17 * * * * php artisan command-two .......... Next Due: 12 minutes from now ``` After: ``` php artisan schedule:list 15 * * * * php artisan command-three ........ Next Due: 10 minutes from now 17 * * * * php artisan command-two .......... Next Due: 12 minutes from now 0 * * * * php artisan command-one .......... Next Due: 55 minutes from now ``` * Add a test * fix styling * need to actually use the flag in the test * formatting Co-authored-by: Taylor Otwell <[email protected]>
1 parent fbd39d9 commit 28d07f0

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

src/Illuminate/Console/Scheduling/ScheduleListCommand.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ class ScheduleListCommand extends Command
2121
*
2222
* @var string
2323
*/
24-
protected $signature = 'schedule:list {--timezone= : The timezone that times should be displayed in}';
24+
protected $signature = 'schedule:list
25+
{--timezone= : The timezone that times should be displayed in}
26+
{--next : Sort the listed tasks by their next due date}
27+
';
2528

2629
/**
2730
* The name of the console command.
@@ -39,7 +42,7 @@ class ScheduleListCommand extends Command
3942
*
4043
* @var string
4144
*/
42-
protected $description = 'List the scheduled commands';
45+
protected $description = 'List all scheduled tasks';
4346

4447
/**
4548
* The terminal width resolver callback.
@@ -72,6 +75,8 @@ public function handle(Schedule $schedule)
7275

7376
$timezone = new DateTimeZone($this->option('timezone') ?? config('app.timezone'));
7477

78+
$events = $this->sortEvents($events, $timezone);
79+
7580
$events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $timezone) {
7681
$expression = $this->formatCronExpression($event->expression, $expressionSpacing);
7782

@@ -98,10 +103,7 @@ public function handle(Schedule $schedule)
98103

99104
$nextDueDateLabel = 'Next Due:';
100105

101-
$nextDueDate = Carbon::create((new CronExpression($event->expression))
102-
->getNextRunDate(Carbon::now()->setTimezone($event->timezone))
103-
->setTimezone($timezone)
104-
);
106+
$nextDueDate = $this->getNextDueDateForEvent($event, $timezone);
105107

106108
$nextDueDate = $this->output->isVerbose()
107109
? $nextDueDate->format('Y-m-d H:i:s P')
@@ -150,6 +152,36 @@ private function getCronExpressionSpacing($events)
150152
return collect($rows[0] ?? [])->keys()->map(fn ($key) => $rows->max($key));
151153
}
152154

155+
/**
156+
* Sorts the events by due date if option set.
157+
*
158+
* @param \Illuminate\Support\Collection $events
159+
* @param \DateTimeZone $timezone
160+
* @return \Illuminate\Support\Collection
161+
*/
162+
private function sortEvents(\Illuminate\Support\Collection $events, DateTimeZone $timezone)
163+
{
164+
return $this->option('next')
165+
? $events->sortBy(fn ($event) => $this->getNextDueDateForEvent($event, $timezone))
166+
: $events;
167+
}
168+
169+
/**
170+
* Get the next due date for an event.
171+
*
172+
* @param \Illuminate\Console\Scheduling\Event $event
173+
* @param \DateTimeZone $timezone
174+
* @return \Illuminate\Support\Carbon
175+
*/
176+
private function getNextDueDateForEvent($event, DateTimeZone $timezone)
177+
{
178+
return Carbon::create(
179+
(new CronExpression($event->expression))
180+
->getNextRunDate(Carbon::now()->setTimezone($event->timezone))
181+
->setTimezone($timezone)
182+
);
183+
}
184+
153185
/**
154186
* Formats the cron expression based on the spacing provided.
155187
*

tests/Integration/Console/Scheduling/ScheduleListCommandTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@ public function testDisplaySchedule()
5252
->expectsOutput(' * * * * * Closure at: '.$closureFilePath.':'.$closureLineNumber.' Next Due: 1 minute from now');
5353
}
5454

55+
public function testDisplayScheduleWithSort()
56+
{
57+
$this->schedule->command(FooCommand::class)->quarterly();
58+
$this->schedule->command('inspire')->twiceDaily(14, 18);
59+
$this->schedule->command('foobar', ['a' => 'b'])->everyMinute();
60+
$this->schedule->job(FooJob::class)->everyMinute();
61+
$this->schedule->command('inspire')->cron('0 9,17 * * *');
62+
$this->schedule->command('inspire')->cron("0 10\t* * *");
63+
64+
$this->schedule->call(fn () => '')->everyMinute();
65+
$closureLineNumber = __LINE__ - 1;
66+
$closureFilePath = __FILE__;
67+
68+
$this->artisan(ScheduleListCommand::class, ['--next' => true])
69+
->assertSuccessful()
70+
->expectsOutput(' * * * * * php artisan foobar a='.ProcessUtils::escapeArgument('b').' ... Next Due: 1 minute from now')
71+
->expectsOutput(' * * * * * Illuminate\Tests\Integration\Console\Scheduling\FooJob Next Due: 1 minute from now')
72+
->expectsOutput(' * * * * * Closure at: '.$closureFilePath.':'.$closureLineNumber.' Next Due: 1 minute from now')
73+
->expectsOutput(' 0 9,17 * * * php artisan inspire ......... Next Due: 9 hours from now')
74+
->expectsOutput(' 0 10 * * * php artisan inspire ........ Next Due: 10 hours from now')
75+
->expectsOutput(' 0 14,18 * * * php artisan inspire ........ Next Due: 14 hours from now')
76+
->expectsOutput(' 0 0 1 1-12/3 * php artisan foo:command .... Next Due: 3 months from now');
77+
}
78+
5579
public function testDisplayScheduleInVerboseMode()
5680
{
5781
$this->schedule->command(FooCommand::class)->everyMinute();

0 commit comments

Comments
 (0)