diff --git a/src/Command/AddUserCommand.php b/src/Command/AddUserCommand.php
index a060f81bf..c9f4d53e6 100644
--- a/src/Command/AddUserCommand.php
+++ b/src/Command/AddUserCommand.php
@@ -15,12 +15,12 @@
use App\Repository\UserRepository;
use App\Utils\Validator;
use Doctrine\ORM\EntityManagerInterface;
+use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
-use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
@@ -49,7 +49,8 @@
*/
#[AsCommand(
name: 'app:add-user',
- description: 'Creates users and stores them in the database'
+ description: 'Creates users and stores them in the database',
+ help: self::HELP,
)]
final class AddUserCommand extends Command
{
@@ -64,23 +65,9 @@ public function __construct(
parent::__construct();
}
- protected function configure(): void
- {
- $this
- ->setHelp($this->getCommandHelp())
- // commands can optionally define arguments and/or options (mandatory and optional)
- // see https://symfony.com/doc/current/components/console/console_arguments.html
- ->addArgument('username', InputArgument::OPTIONAL, 'The username of the new user')
- ->addArgument('password', InputArgument::OPTIONAL, 'The plain password of the new user')
- ->addArgument('email', InputArgument::OPTIONAL, 'The email of the new user')
- ->addArgument('full-name', InputArgument::OPTIONAL, 'The full name of the new user')
- ->addOption('admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator')
- ;
- }
-
/**
- * This optional method is the first one executed for a command after configure()
- * and is useful to initialize properties based on the input arguments and options.
+ * This optional method is the first one executed for a command and is useful
+ * to initialize properties based on the input arguments and options.
*/
protected function initialize(InputInterface $input, OutputInterface $output): void
{
@@ -91,9 +78,9 @@ protected function initialize(InputInterface $input, OutputInterface $output): v
}
/**
- * This method is executed after initialize() and before execute(). Its purpose
- * is to check if some of the options/arguments are missing and interactively
- * ask the user for those values.
+ * This method is executed after initialize() and before __invoke(). Its purpose
+ * is to check if some options/arguments are missing and interactively ask the user
+ * for those values.
*
* This method is completely optional. If you are developing an internal console
* command, you probably should not implement this method because it requires
@@ -161,26 +148,21 @@ protected function interact(InputInterface $input, OutputInterface $output): voi
/**
* This method is executed after interact() and initialize(). It usually
* contains the logic to execute to complete this command task.
+ *
+ * Commands can optionally define arguments and/or options (mandatory and optional)
+ *
+ * @see https://symfony.com/doc/current/console/input.html
*/
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
+ public function __invoke(
+ #[Argument('The username of the new user')] string $username,
+ #[Argument('The plain password of the new user', 'password')] string $plainPassword,
+ #[Argument('The email of the new user')] string $email,
+ #[Argument('The full name of the new user')] string $fullName,
+ #[Option('If set, the user is created as an administrator', 'admin')] bool $isAdmin = false,
+ ): int {
$stopwatch = new Stopwatch();
$stopwatch->start('add-user-command');
- /** @var string $username */
- $username = $input->getArgument('username');
-
- /** @var string $plainPassword */
- $plainPassword = $input->getArgument('password');
-
- /** @var string $email */
- $email = $input->getArgument('email');
-
- /** @var string $fullName */
- $fullName = $input->getArgument('full-name');
-
- $isAdmin = $input->getOption('admin');
-
// make sure to validate the user data is correct
$this->validateUserData($username, $plainPassword, $email, $fullName);
@@ -202,7 +184,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$event = $stopwatch->stop('add-user-command');
- if ($output->isVerbose()) {
+ if ($this->io->isVerbose()) {
$this->io->comment(\sprintf('New user database id: %d / Elapsed time: %.2f ms / Consumed memory: %.2f MB', $user->getId(), $event->getDuration(), $event->getMemory() / (1024 ** 2)));
}
@@ -232,33 +214,30 @@ private function validateUserData(string $username, string $plainPassword, strin
}
/**
- * The command help is usually included in the configure() method, but when
- * it's too long, it's better to define a separate method to maintain the
+ * The command help is usually included in the #[AsCommand] attribute, but when
+ * it's too long, it's better to define a separate constant to maintain the
* code readability.
*/
- private function getCommandHelp(): string
- {
- return <<<'HELP'
- The %command.name% command creates new users and saves them in the database:
+ public const HELP = <<<'HELP'
+ The %command.name% command creates new users and saves them in the database:
- php %command.full_name% username password email
+ php %command.full_name% username password email
- By default the command creates regular users. To create administrator users,
- add the --admin option:
+ By default the command creates regular users. To create administrator users,
+ add the --admin option:
- php %command.full_name% username password email --admin
+ php %command.full_name% username password email --admin
- If you omit any of the three required arguments, the command will ask you to
- provide the missing values:
+ If you omit any of the three required arguments, the command will ask you to
+ provide the missing values:
- # command will ask you for the email
- php %command.full_name% username password
+ # command will ask you for the email
+ php %command.full_name% username password
- # command will ask you for the email and password
- php %command.full_name% username
+ # command will ask you for the email and password
+ php %command.full_name% username
- # command will ask you for all arguments
- php %command.full_name%
- HELP;
- }
+ # command will ask you for all arguments
+ php %command.full_name%
+ HELP;
}
diff --git a/src/Command/DeleteUserCommand.php b/src/Command/DeleteUserCommand.php
index 2c91523d5..39d9d7c72 100644
--- a/src/Command/DeleteUserCommand.php
+++ b/src/Command/DeleteUserCommand.php
@@ -16,10 +16,10 @@
use App\Utils\Validator;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
-use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
@@ -41,7 +41,17 @@
*/
#[AsCommand(
name: 'app:delete-user',
- description: 'Deletes users from the database'
+ description: 'Deletes users from the database',
+ help: <<<'HELP'
+ The %command.name% command deletes users from the database:
+
+ php %command.full_name% username
+
+ If you omit the argument, the command will ask you to
+ provide the missing value:
+
+ php %command.full_name%
+ HELP,
)]
final class DeleteUserCommand extends Command
{
@@ -56,23 +66,6 @@ public function __construct(
parent::__construct();
}
- protected function configure(): void
- {
- $this
- ->addArgument('username', InputArgument::REQUIRED, 'The username of an existing user')
- ->setHelp(<<<'HELP'
- The %command.name% command deletes users from the database:
-
- php %command.full_name% username
-
- If you omit the argument, the command will ask you to
- provide the missing value:
-
- php %command.full_name%
- HELP
- );
- }
-
protected function initialize(InputInterface $input, OutputInterface $output): void
{
// SymfonyStyle is an optional feature that Symfony provides so you can
@@ -105,10 +98,8 @@ protected function interact(InputInterface $input, OutputInterface $output): voi
$input->setArgument('username', $username);
}
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(#[Argument('The username of an existing user')] string $username): int
{
- /** @var string|null $username */
- $username = $input->getArgument('username');
$username = $this->validator->validateUsername($username);
/** @var User|null $user */
diff --git a/src/Command/ListUsersCommand.php b/src/Command/ListUsersCommand.php
index 41e8c259f..cfecb44fa 100644
--- a/src/Command/ListUsersCommand.php
+++ b/src/Command/ListUsersCommand.php
@@ -14,9 +14,9 @@
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
@@ -42,9 +42,24 @@
#[AsCommand(
name: 'app:list-users',
description: 'Lists all the existing users',
- aliases: ['app:users']
+ aliases: ['app:users'],
+ help: <<<'HELP'
+ The %command.name% command lists all the users registered in the application:
+
+ php %command.full_name%
+
+ By default the command only displays the 50 most recent users. Set the number of
+ results to display with the --max-results option:
+
+ php %command.full_name% --max-results=2000
+
+ In addition to displaying the user list, you can also send this information to
+ the email address specified in the --send-to option:
+
+ php %command.full_name% --send-to=fabien@symfony.com
+ HELP,
)]
-final class ListUsersCommand extends Command
+final class ListUsersCommand
{
public function __construct(
private readonly MailerInterface $mailer,
@@ -52,44 +67,22 @@ public function __construct(
private readonly string $emailSender,
private readonly UserRepository $users,
) {
- parent::__construct();
- }
-
- protected function configure(): void
- {
- $this
- ->setHelp(<<<'HELP'
- The %command.name% command lists all the users registered in the application:
-
- php %command.full_name%
-
- By default the command only displays the 50 most recent users. Set the number of
- results to display with the --max-results option:
-
- php %command.full_name% --max-results=2000
-
- In addition to displaying the user list, you can also send this information to
- the email address specified in the --send-to option:
-
- php %command.full_name% --send-to=fabien@symfony.com
- HELP
- )
- // commands can optionally define arguments and/or options (mandatory and optional)
- // see https://symfony.com/doc/current/components/console/console_arguments.html
- ->addOption('max-results', null, InputOption::VALUE_OPTIONAL, 'Limits the number of users listed', 50)
- ->addOption('send-to', null, InputOption::VALUE_OPTIONAL, 'If set, the result is sent to the given email address')
- ;
}
/**
* This method is executed after initialize(). It usually contains the logic
* to execute to complete this command task.
+ *
+ * Commands can optionally define arguments and/or options (mandatory and optional)
+ *
+ * @see https://symfony.com/doc/current/console/input.html
*/
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- /** @var int|null $maxResults */
- $maxResults = $input->getOption('max-results');
-
+ public function __invoke(
+ InputInterface $input,
+ OutputInterface $output,
+ #[Option('If set, the result is sent to the given email address', 'send-to')] ?string $email = null,
+ #[Option('Limits the number of users listed')] int $maxResults = 50,
+ ): int {
// Use ->findBy() instead of ->findAll() to allow result sorting and limiting
$allUsers = $this->users->findBy([], ['id' => 'DESC'], $maxResults);
@@ -122,9 +115,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$usersAsATable = $bufferedOutput->fetch();
$output->write($usersAsATable);
- /** @var string|null $email */
- $email = $input->getOption('send-to');
-
if (null !== $email) {
$this->sendReport($usersAsATable, $email);
}
diff --git a/src/Controller/.gitignore b/src/Controller/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/Entity/.gitignore b/src/Entity/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/Repository/.gitignore b/src/Repository/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/tests/Command/AbstractCommandTestCase.php b/tests/Command/AbstractCommandTestCase.php
index b2f279336..43aa28388 100644
--- a/tests/Command/AbstractCommandTestCase.php
+++ b/tests/Command/AbstractCommandTestCase.php
@@ -13,7 +13,6 @@
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
-use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
abstract class AbstractCommandTestCase extends KernelTestCase
@@ -29,11 +28,10 @@ abstract class AbstractCommandTestCase extends KernelTestCase
protected function executeCommand(array $arguments, array $inputs = []): CommandTester
{
$kernel = self::bootKernel();
+ $application = new Application($kernel);
- // this uses a special testing container that allows you to fetch private services
- /** @var Command $command */
- $command = static::getContainer()->get($this->getCommandFqcn());
- $command->setApplication(new Application($kernel));
+ $command = $application->find($this->getCommandName());
+ $command->setApplication($application);
$commandTester = new CommandTester($command);
$commandTester->setInputs($inputs);
@@ -42,5 +40,5 @@ protected function executeCommand(array $arguments, array $inputs = []): Command
return $commandTester;
}
- abstract protected function getCommandFqcn(): string;
+ abstract protected function getCommandName(): string;
}
diff --git a/tests/Command/AddUserCommandTest.php b/tests/Command/AddUserCommandTest.php
index d8f20e2a9..979bffbf4 100644
--- a/tests/Command/AddUserCommandTest.php
+++ b/tests/Command/AddUserCommandTest.php
@@ -11,7 +11,6 @@
namespace App\Tests\Command;
-use App\Command\AddUserCommand;
use App\Repository\UserRepository;
use PHPUnit\Framework\Attributes\DataProvider;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
@@ -103,8 +102,8 @@ private function assertUserCreated(bool $isAdmin): void
$this->assertSame($isAdmin ? ['ROLE_ADMIN'] : ['ROLE_USER'], $user->getRoles());
}
- protected function getCommandFqcn(): string
+ protected function getCommandName(): string
{
- return AddUserCommand::class;
+ return 'app:add-user';
}
}
diff --git a/tests/Command/ListUsersCommandTest.php b/tests/Command/ListUsersCommandTest.php
index 4726901b8..e41e4bdfc 100644
--- a/tests/Command/ListUsersCommandTest.php
+++ b/tests/Command/ListUsersCommandTest.php
@@ -11,7 +11,6 @@
namespace App\Tests\Command;
-use App\Command\ListUsersCommand;
use PHPUnit\Framework\Attributes\DataProvider;
final class ListUsersCommandTest extends AbstractCommandTestCase
@@ -50,8 +49,8 @@ public function testItSendsAnEmailIfOptionProvided(): void
$this->assertEmailCount(1);
}
- protected function getCommandFqcn(): string
+ protected function getCommandName(): string
{
- return ListUsersCommand::class;
+ return 'app:list-users';
}
}