diff --git a/src/Illuminate/Console/Concerns/PromptsForMissingInput.php b/src/Illuminate/Console/Concerns/PromptsForMissingInput.php index 59a92d162d70..c4b5f96e0dd0 100644 --- a/src/Illuminate/Console/Concerns/PromptsForMissingInput.php +++ b/src/Illuminate/Console/Concerns/PromptsForMissingInput.php @@ -4,6 +4,8 @@ use Closure; use Illuminate\Contracts\Console\PromptsForMissingInput as PromptsForMissingInputContract; +use Illuminate\Support\Arr; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -37,25 +39,30 @@ protected function interact(InputInterface $input, OutputInterface $output) protected function promptForMissingArguments(InputInterface $input, OutputInterface $output) { $prompted = collect($this->getDefinition()->getArguments()) - ->filter(fn ($argument) => $argument->isRequired() && is_null($input->getArgument($argument->getName()))) - ->filter(fn ($argument) => $argument->getName() !== 'command') - ->each(function ($argument) use ($input) { + ->reject(fn (InputArgument $argument) => $argument->getName() === 'command') + ->filter(fn (InputArgument $argument) => $argument->isRequired() && match (true) { + $argument->isArray() => empty($input->getArgument($argument->getName())), + default => is_null($input->getArgument($argument->getName())), + }) + ->each(function (InputArgument $argument) use ($input) { $label = $this->promptForMissingArgumentsUsing()[$argument->getName()] ?? 'What is '.lcfirst($argument->getDescription() ?: ('the '.$argument->getName())).'?'; if ($label instanceof Closure) { - return $input->setArgument($argument->getName(), $label()); + return $input->setArgument($argument->getName(), $argument->isArray() ? Arr::wrap($label()) : $label()); } if (is_array($label)) { [$label, $placeholder] = $label; } - $input->setArgument($argument->getName(), text( + $answer = text( label: $label, placeholder: $placeholder ?? '', validate: fn ($value) => empty($value) ? "The {$argument->getName()} is required." : null, - )); + ); + + $input->setArgument($argument->getName(), $argument->isArray() ? [$answer] : $answer); }) ->isNotEmpty(); diff --git a/tests/Console/ConsoleApplicationTest.php b/tests/Console/ConsoleApplicationTest.php index 9edc18accf65..b3ffdf7fa9e4 100755 --- a/tests/Console/ConsoleApplicationTest.php +++ b/tests/Console/ConsoleApplicationTest.php @@ -8,6 +8,7 @@ use Illuminate\Contracts\Foundation\Application as ApplicationContract; use Illuminate\Events\Dispatcher as EventsDispatcher; use Illuminate\Foundation\Application as FoundationApplication; +use Illuminate\Tests\Console\Fixtures\FakeCommandWithArrayInputPrompting; use Illuminate\Tests\Console\Fixtures\FakeCommandWithInputPrompting; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -160,6 +161,7 @@ public function testCommandInputPromptsWhenRequiredArgumentIsMissing() $statusCode = $app->call('fake-command-for-testing'); $this->assertTrue($command->prompted); + $this->assertSame('foo', $command->argument('name')); $this->assertSame(0, $statusCode); } @@ -178,6 +180,45 @@ public function testCommandInputDoesntPromptWhenRequiredArgumentIsPassed() ]); $this->assertFalse($command->prompted); + $this->assertSame('foo', $command->argument('name')); + $this->assertSame(0, $statusCode); + } + + public function testCommandInputPromptsWhenRequiredArgumentsAreMissing() + { + $app = new Application( + $laravel = new \Illuminate\Foundation\Application(__DIR__), + $events = m::mock(Dispatcher::class, ['dispatch' => null, 'fire' => null]), + 'testing' + ); + + $app->addCommands([$command = new FakeCommandWithArrayInputPrompting()]); + + $command->setLaravel($laravel); + + $statusCode = $app->call('fake-command-for-testing-array'); + + $this->assertTrue($command->prompted); + $this->assertSame(['foo'], $command->argument('names')); + $this->assertSame(0, $statusCode); + } + + public function testCommandInputDoesntPromptWhenRequiredArgumentsArePassed() + { + $app = new Application( + $app = new \Illuminate\Foundation\Application(__DIR__), + $events = m::mock(Dispatcher::class, ['dispatch' => null, 'fire' => null]), + 'testing' + ); + + $app->addCommands([$command = new FakeCommandWithArrayInputPrompting()]); + + $statusCode = $app->call('fake-command-for-testing-array', [ + 'names' => ['foo', 'bar', 'baz'], + ]); + + $this->assertFalse($command->prompted); + $this->assertSame(['foo', 'bar', 'baz'], $command->argument('names')); $this->assertSame(0, $statusCode); } diff --git a/tests/Console/Fixtures/FakeCommandWithArrayInputPrompting.php b/tests/Console/Fixtures/FakeCommandWithArrayInputPrompting.php new file mode 100644 index 000000000000..36373eddf8da --- /dev/null +++ b/tests/Console/Fixtures/FakeCommandWithArrayInputPrompting.php @@ -0,0 +1,33 @@ +prompted = true; + + return 'foo'; + }); + } + + public function handle(): int + { + return self::SUCCESS; + } +}