264 lines
10 KiB
PHP
264 lines
10 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Symfony package.
|
|
*
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace App\Command;
|
|
|
|
use App\Entity\User;
|
|
use App\Repository\UserRepository;
|
|
use App\Utils\Validator;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
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\Security\Core\Encoder\UserPasswordEncoderInterface;
|
|
use Symfony\Component\Stopwatch\Stopwatch;
|
|
use function Symfony\Component\String\u;
|
|
|
|
/**
|
|
* A console command that creates users and stores them in the database.
|
|
*
|
|
* To use this command, open a terminal window, enter into your project
|
|
* directory and execute the following:
|
|
*
|
|
* $ php bin/console app:add-user
|
|
*
|
|
* To output detailed information, increase the command verbosity:
|
|
*
|
|
* $ php bin/console app:add-user -vv
|
|
*
|
|
* See https://symfony.com/doc/current/console.html
|
|
*
|
|
* We use the default services.yaml configuration, so command classes are registered as services.
|
|
* See https://symfony.com/doc/current/console/commands_as_services.html
|
|
*
|
|
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
|
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
|
*/
|
|
class AddUserCommand extends Command
|
|
{
|
|
// to make your command lazily loaded, configure the $defaultName static property,
|
|
// so it will be instantiated only when the command is actually called.
|
|
protected static $defaultName = 'app:add-user';
|
|
|
|
/**
|
|
* @var SymfonyStyle
|
|
*/
|
|
private $io;
|
|
|
|
private $entityManager;
|
|
private $passwordEncoder;
|
|
private $validator;
|
|
private $users;
|
|
|
|
public function __construct(EntityManagerInterface $em, UserPasswordEncoderInterface $encoder, Validator $validator, UserRepository $users)
|
|
{
|
|
parent::__construct();
|
|
|
|
$this->entityManager = $em;
|
|
$this->passwordEncoder = $encoder;
|
|
$this->validator = $validator;
|
|
$this->users = $users;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected function configure(): void
|
|
{
|
|
$this
|
|
->setDescription('Creates users and stores them in the database')
|
|
->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.
|
|
*/
|
|
protected function initialize(InputInterface $input, OutputInterface $output): void
|
|
{
|
|
// 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
|
|
* 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
|
|
* quite a lot of work. However, if the command is meant to be used by external
|
|
* users, this method is a nice way to fall back and prevent errors.
|
|
*/
|
|
protected function interact(InputInterface $input, OutputInterface $output)
|
|
{
|
|
if (null !== $input->getArgument('username') && null !== $input->getArgument('password') && null !== $input->getArgument('email') && null !== $input->getArgument('full-name')) {
|
|
return;
|
|
}
|
|
|
|
$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',
|
|
'',
|
|
'Now we\'ll ask you for the value of all the missing command arguments.',
|
|
]);
|
|
|
|
// Ask for the username if it's not defined
|
|
$username = $input->getArgument('username');
|
|
if (null !== $username) {
|
|
$this->io->text(' > <info>Username</info>: '.$username);
|
|
} else {
|
|
$username = $this->io->ask('Username', null, [$this->validator, 'validateUsername']);
|
|
$input->setArgument('username', $username);
|
|
}
|
|
|
|
// Ask for the password if it's not defined
|
|
$password = $input->getArgument('password');
|
|
if (null !== $password) {
|
|
$this->io->text(' > <info>Password</info>: '.u('*')->repeat(u($password)->length()));
|
|
} else {
|
|
$password = $this->io->askHidden('Password (your type will be hidden)', [$this->validator, 'validatePassword']);
|
|
$input->setArgument('password', $password);
|
|
}
|
|
|
|
// Ask for the email if it's not defined
|
|
$email = $input->getArgument('email');
|
|
if (null !== $email) {
|
|
$this->io->text(' > <info>Email</info>: '.$email);
|
|
} else {
|
|
$email = $this->io->ask('Email', null, [$this->validator, 'validateEmail']);
|
|
$input->setArgument('email', $email);
|
|
}
|
|
|
|
// Ask for the full name if it's not defined
|
|
$fullName = $input->getArgument('full-name');
|
|
if (null !== $fullName) {
|
|
$this->io->text(' > <info>Full Name</info>: '.$fullName);
|
|
} else {
|
|
$fullName = $this->io->ask('Full Name', null, [$this->validator, 'validateFullName']);
|
|
$input->setArgument('full-name', $fullName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is executed after interact() and initialize(). It usually
|
|
* contains the logic to execute to complete this command task.
|
|
*/
|
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
{
|
|
$stopwatch = new Stopwatch();
|
|
$stopwatch->start('add-user-command');
|
|
|
|
$username = $input->getArgument('username');
|
|
$plainPassword = $input->getArgument('password');
|
|
$email = $input->getArgument('email');
|
|
$fullName = $input->getArgument('full-name');
|
|
$isAdmin = $input->getOption('admin');
|
|
|
|
// make sure to validate the user data is correct
|
|
$this->validateUserData($username, $plainPassword, $email, $fullName);
|
|
|
|
// create the user and encode its password
|
|
$user = new User();
|
|
$user->setFullName($fullName);
|
|
$user->setUsername($username);
|
|
$user->setEmail($email);
|
|
$user->setRoles([$isAdmin ? 'ROLE_ADMIN' : 'ROLE_USER']);
|
|
|
|
// See https://symfony.com/doc/current/security.html#c-encoding-passwords
|
|
$encodedPassword = $this->passwordEncoder->encodePassword($user, $plainPassword);
|
|
$user->setPassword($encodedPassword);
|
|
|
|
$this->entityManager->persist($user);
|
|
$this->entityManager->flush();
|
|
|
|
$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()) {
|
|
$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)));
|
|
}
|
|
|
|
return Command::SUCCESS;
|
|
}
|
|
|
|
private function validateUserData($username, $plainPassword, $email, $fullName): void
|
|
{
|
|
// first check if a user with the same username already exists.
|
|
$existingUser = $this->users->findOneBy(['username' => $username]);
|
|
|
|
if (null !== $existingUser) {
|
|
throw new RuntimeException(sprintf('There is already a user registered with the "%s" username.', $username));
|
|
}
|
|
|
|
// validate password and email if is not this input means interactive.
|
|
$this->validator->validatePassword($plainPassword);
|
|
$this->validator->validateEmail($email);
|
|
$this->validator->validateFullName($fullName);
|
|
|
|
// check if a user with the same email already exists.
|
|
$existingEmail = $this->users->findOneBy(['email' => $email]);
|
|
|
|
if (null !== $existingEmail) {
|
|
throw new RuntimeException(sprintf('There is already a user registered with the "%s" email.', $email));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* code readability.
|
|
*/
|
|
private function getCommandHelp(): string
|
|
{
|
|
return <<<'HELP'
|
|
The <info>%command.name%</info> command creates new users and saves them in the database:
|
|
|
|
<info>php %command.full_name%</info> <comment>username password email</comment>
|
|
|
|
By default the command creates regular users. To create administrator users,
|
|
add the <comment>--admin</comment> option:
|
|
|
|
<info>php %command.full_name%</info> username password email <comment>--admin</comment>
|
|
|
|
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
|
|
<info>php %command.full_name%</info> <comment>username password</comment>
|
|
|
|
# command will ask you for the email and password
|
|
<info>php %command.full_name%</info> <comment>username</comment>
|
|
|
|
# command will ask you for all arguments
|
|
<info>php %command.full_name%</info>
|
|
|
|
HELP;
|
|
}
|
|
}
|