diff --git a/src/AppBundle/Command/AddUserCommand.php b/src/AppBundle/Command/AddUserCommand.php index d263434fe..17e0744a4 100644 --- a/src/AppBundle/Command/AddUserCommand.php +++ b/src/AppBundle/Command/AddUserCommand.php @@ -18,9 +18,9 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; +use Symfony\Component\Stopwatch\Stopwatch; /** * A command console that creates users and stores them in the database. @@ -45,6 +45,7 @@ class AddUserCommand extends Command { const MAX_ATTEMPTS = 5; + private $io; private $entityManager; private $passwordEncoder; @@ -76,6 +77,18 @@ protected function configure() ; } + /** + * 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. + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + // SymfonyStyle is an optional feature that Symfony provides so you can + // apply a consistent look to the commands of your application. + // See https://symfony.com/doc/current/console/style.html + $this->io = new SymfonyStyle($input, $output); + } + /** * This method is executed after initialize() and before execute(). Its purpose * is to check if some of the options/arguments are missing and interactively @@ -92,18 +105,10 @@ protected function interact(InputInterface $input, OutputInterface $output) return; } - // See: http://symfony.com/doc/current/console/style.html - $io = new SymfonyStyle($input, $output); - - // Use the title() method to display the title - $io->title('Add User Command Interactive Wizard'); - - // multi-line messages can be displayed this way... - $io->text('If you prefer to not use this interactive wizard, provide the'); - $io->text('arguments required by this command as follows:'); - - // ...but you can also pass an array of strings to the text() method - $io->text([ + $this->io->title('Add User Command Interactive Wizard'); + $this->io->text([ + 'If you prefer to not use this interactive wizard, provide the', + 'arguments required by this command as follows:', '', ' $ php bin/console app:add-user username password email@example.com', '', @@ -112,61 +117,45 @@ protected function interact(InputInterface $input, OutputInterface $output) // Ask for the username if it's not defined $username = $input->getArgument('username'); - if (null === $username) { - $question = new Question('Username'); - $question->setValidator(function ($answer) { + if (null !== $username) { + $this->io->text(' > Username: '.$username); + } else { + $username = $this->io->ask('Username', null, function ($answer) { if (empty($answer)) { throw new \RuntimeException('The username cannot be empty'); } return $answer; }); - $question->setMaxAttempts(self::MAX_ATTEMPTS); - $username = $io->askQuestion($question); $input->setArgument('username', $username); - } else { - $io->text(' > Username: '.$username); } // Ask for the password if it's not defined $password = $input->getArgument('password'); - if (null === $password) { - $question = new Question('Password (your type will be hidden)'); - $question->setValidator([$this, 'passwordValidator']); - $question->setHidden(true); - $question->setMaxAttempts(self::MAX_ATTEMPTS); - - $password = $io->askQuestion($question); - $input->setArgument('password', $password); + if (null !== $password) { + $this->io->text(' > Password: '.str_repeat('*', mb_strlen($password))); } else { - $io->text(' > Password: '.str_repeat('*', mb_strlen($password))); + $password = $this->io->askHidden('Password (your type will be hidden)', null, [$this, 'passwordValidator']); + $input->setArgument('password', $password); } // Ask for the email if it's not defined $email = $input->getArgument('email'); - if (null === $email) { - $question = new Question('Email'); - $question->setValidator([$this, 'emailValidator']); - $question->setMaxAttempts(self::MAX_ATTEMPTS); - - $email = $io->askQuestion($question); - $input->setArgument('email', $email); + if (null !== $email) { + $this->io->text(' > Email: '.$email); } else { - $io->text(' > Email: '.$email); + $email = $this->io->ask('Email', null, [$this, 'emailValidator']); + $input->setArgument('email', $email); } // Ask for the full name if it's not defined $fullName = $input->getArgument('full-name'); - if (null === $fullName) { - $question = new Question('Full Name'); - $question->setValidator([$this, 'fullNameValidator']); - $question->setMaxAttempts(self::MAX_ATTEMPTS); - - $fullName = $io->askQuestion($question); - $input->setArgument('full-name', $fullName); + if (null !== $fullName) { + $this->io->text(' > Full Name: '.$fullName); } else { - $io->text(' > Full Name: '.$fullName); + $fullName = $this->io->ask('Full Name', null, [$this, 'fullNameValidator']); + $input->setArgument('full-name', $fullName); } } @@ -176,8 +165,8 @@ protected function interact(InputInterface $input, OutputInterface $output) */ protected function execute(InputInterface $input, OutputInterface $output) { - $startTime = microtime(true); - $io = new SymfonyStyle($input, $output); + $stopwatch = new Stopwatch(); + $stopwatch->start('add-user-command'); $username = $input->getArgument('username'); $plainPassword = $input->getArgument('password'); @@ -202,13 +191,11 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->entityManager->persist($user); $this->entityManager->flush(); - $io->success(sprintf('%s was successfully created: %s (%s)', $isAdmin ? 'Administrator user' : 'User', $user->getUsername(), $user->getEmail())); + $this->io->success(sprintf('%s was successfully created: %s (%s)', $isAdmin ? 'Administrator user' : 'User', $user->getUsername(), $user->getEmail())); + $event = $stopwatch->stop('add-user-command'); if ($output->isVerbose()) { - $finishTime = microtime(true); - $elapsedTime = $finishTime - $startTime; - - $io->note(sprintf('New user database id: %d / Elapsed time: %.2f ms', $user->getId(), $elapsedTime * 1000)); + $this->io->comment(sprintf('New user database id: %d / Elapsed time: %.2f ms / Consumed memory: %.2f MB', $user->getId(), $event->getDuration(), $event->getMemory() / pow(1024, 2))); } } diff --git a/src/AppBundle/Command/DeleteUserCommand.php b/src/AppBundle/Command/DeleteUserCommand.php index 060357908..a89db0ecb 100644 --- a/src/AppBundle/Command/DeleteUserCommand.php +++ b/src/AppBundle/Command/DeleteUserCommand.php @@ -40,6 +40,7 @@ class DeleteUserCommand extends Command { const MAX_ATTEMPTS = 5; + private $io; private $entityManager; public function __construct(EntityManagerInterface $em) @@ -71,18 +72,22 @@ protected function configure() ); } + protected function initialize(InputInterface $input, OutputInterface $output) + { + // SymfonyStyle is an optional feature that Symfony provides so you can + // apply a consistent look to the commands of your application. + // See https://symfony.com/doc/current/console/style.html + $this->io = new SymfonyStyle($input, $output); + } + protected function interact(InputInterface $input, OutputInterface $output) { if (null !== $input->getArgument('username')) { return; } - // See: http://symfony.com/doc/current/console/style.html - $io = new SymfonyStyle($input, $output); - - $io->title('Delete User Command Interactive Wizard'); - - $io->text([ + $this->io->title('Delete User Command Interactive Wizard'); + $this->io->text([ 'If you prefer to not use this interactive wizard, provide the', 'arguments required by this command as follows:', '', @@ -92,8 +97,7 @@ protected function interact(InputInterface $input, OutputInterface $output) '', ]); - $username = $io->ask('Username', null, [$this, 'usernameValidator']); - + $username = $this->io->ask('Username', null, [$this, 'usernameValidator']); $input->setArgument('username', $username); } @@ -118,8 +122,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->entityManager->remove($user); $this->entityManager->flush(); - (new SymfonyStyle($input, $output)) - ->success(sprintf('User "%s" (ID: %d, email: %s) was successfully deleted.', $user->getUsername(), $userId, $user->getEmail())); + $this->io->success(sprintf('User "%s" (ID: %d, email: %s) was successfully deleted.', $user->getUsername(), $userId, $user->getEmail())); } /** diff --git a/src/AppBundle/Command/ListUsersCommand.php b/src/AppBundle/Command/ListUsersCommand.php index 11e699088..59950debe 100644 --- a/src/AppBundle/Command/ListUsersCommand.php +++ b/src/AppBundle/Command/ListUsersCommand.php @@ -14,11 +14,11 @@ use AppBundle\Entity\User; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Helper\Table; 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; /** * A command console that lists all the existing users. @@ -94,34 +94,34 @@ protected function execute(InputInterface $input, OutputInterface $output) // Doctrine query returns an array of objects and we need an array of plain arrays $usersAsPlainArrays = array_map(function (User $user) { - return [$user->getId(), $user->getFullName(), $user->getUsername(), $user->getEmail(), implode(', ', $user->getRoles())]; + return [ + $user->getId(), + $user->getFullName(), + $user->getUsername(), + $user->getEmail(), + implode(', ', $user->getRoles()), + ]; }, $users); // In your console commands you should always use the regular output type, // which outputs contents directly in the console window. However, this - // particular command uses the BufferedOutput type instead. - // The reason is that the table displaying the list of users can be sent - // via email if the '--send-to' option is provided. Instead of complicating - // things, the BufferedOutput allows to get the command output and store - // it in a variable before displaying it. + // command uses the BufferedOutput type instead, to be able to get the output + // contents before displaying them. This is needed because the command allows + // to send the list of users via email with the '--send-to' option $bufferedOutput = new BufferedOutput(); + $io = new SymfonyStyle($input, $bufferedOutput); + $io->table( + ['ID', 'Full Name', 'Username', 'Email', 'Roles'], + $usersAsPlainArrays + ); - $table = new Table($bufferedOutput); - $table - ->setHeaders(['ID', 'Full Name', 'Username', 'Email', 'Roles']) - ->setRows($usersAsPlainArrays) - ->setStyle(clone Table::getStyleDefinition('symfony-style-guide')) - ; - $table->render(); - - // instead of displaying the table of users, store it in a variable - $tableContents = $bufferedOutput->fetch(); + // instead of just displaying the table of users, store its contents in a variable + $usersAsATable = $bufferedOutput->fetch(); + $output->write($usersAsATable); if (null !== $email = $input->getOption('send-to')) { - $this->sendReport($tableContents, $email); + $this->sendReport($usersAsATable, $email); } - - $output->writeln($tableContents); } /**