Skip to content

Commit

Permalink
Add sort option for schedule:list (#45198)
Browse files Browse the repository at this point in the history
* 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 <taylor@laravel.com>
  • Loading branch information
moshe-autoleadstar and taylorotwell committed Dec 7, 2022
1 parent fbd39d9 commit 28d07f0
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 6 deletions.
44 changes: 38 additions & 6 deletions src/Illuminate/Console/Scheduling/ScheduleListCommand.php
Expand Up @@ -21,7 +21,10 @@ class ScheduleListCommand extends Command
*
* @var string
*/
protected $signature = 'schedule:list {--timezone= : The timezone that times should be displayed in}';
protected $signature = 'schedule:list
{--timezone= : The timezone that times should be displayed in}
{--next : Sort the listed tasks by their next due date}
';

/**
* The name of the console command.
Expand All @@ -39,7 +42,7 @@ class ScheduleListCommand extends Command
*
* @var string
*/
protected $description = 'List the scheduled commands';
protected $description = 'List all scheduled tasks';

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

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

$events = $this->sortEvents($events, $timezone);

$events = $events->map(function ($event) use ($terminalWidth, $expressionSpacing, $timezone) {
$expression = $this->formatCronExpression($event->expression, $expressionSpacing);

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

$nextDueDateLabel = 'Next Due:';

$nextDueDate = Carbon::create((new CronExpression($event->expression))
->getNextRunDate(Carbon::now()->setTimezone($event->timezone))
->setTimezone($timezone)
);
$nextDueDate = $this->getNextDueDateForEvent($event, $timezone);

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

/**
* Sorts the events by due date if option set.
*
* @param \Illuminate\Support\Collection $events
* @param \DateTimeZone $timezone
* @return \Illuminate\Support\Collection
*/
private function sortEvents(\Illuminate\Support\Collection $events, DateTimeZone $timezone)
{
return $this->option('next')
? $events->sortBy(fn ($event) => $this->getNextDueDateForEvent($event, $timezone))
: $events;
}

/**
* Get the next due date for an event.
*
* @param \Illuminate\Console\Scheduling\Event $event
* @param \DateTimeZone $timezone
* @return \Illuminate\Support\Carbon
*/
private function getNextDueDateForEvent($event, DateTimeZone $timezone)
{
return Carbon::create(
(new CronExpression($event->expression))
->getNextRunDate(Carbon::now()->setTimezone($event->timezone))
->setTimezone($timezone)
);
}

/**
* Formats the cron expression based on the spacing provided.
*
Expand Down
24 changes: 24 additions & 0 deletions tests/Integration/Console/Scheduling/ScheduleListCommandTest.php
Expand Up @@ -52,6 +52,30 @@ public function testDisplaySchedule()
->expectsOutput(' * * * * * Closure at: '.$closureFilePath.':'.$closureLineNumber.' Next Due: 1 minute from now');
}

public function testDisplayScheduleWithSort()
{
$this->schedule->command(FooCommand::class)->quarterly();
$this->schedule->command('inspire')->twiceDaily(14, 18);
$this->schedule->command('foobar', ['a' => 'b'])->everyMinute();
$this->schedule->job(FooJob::class)->everyMinute();
$this->schedule->command('inspire')->cron('0 9,17 * * *');
$this->schedule->command('inspire')->cron("0 10\t* * *");

$this->schedule->call(fn () => '')->everyMinute();
$closureLineNumber = __LINE__ - 1;
$closureFilePath = __FILE__;

$this->artisan(ScheduleListCommand::class, ['--next' => true])
->assertSuccessful()
->expectsOutput(' * * * * * php artisan foobar a='.ProcessUtils::escapeArgument('b').' ... Next Due: 1 minute from now')
->expectsOutput(' * * * * * Illuminate\Tests\Integration\Console\Scheduling\FooJob Next Due: 1 minute from now')
->expectsOutput(' * * * * * Closure at: '.$closureFilePath.':'.$closureLineNumber.' Next Due: 1 minute from now')
->expectsOutput(' 0 9,17 * * * php artisan inspire ......... Next Due: 9 hours from now')
->expectsOutput(' 0 10 * * * php artisan inspire ........ Next Due: 10 hours from now')
->expectsOutput(' 0 14,18 * * * php artisan inspire ........ Next Due: 14 hours from now')
->expectsOutput(' 0 0 1 1-12/3 * php artisan foo:command .... Next Due: 3 months from now');
}

public function testDisplayScheduleInVerboseMode()
{
$this->schedule->command(FooCommand::class)->everyMinute();
Expand Down

0 comments on commit 28d07f0

Please sign in to comment.