[UserBundle] Move classes from Command to Console/Command

This commit is contained in:
Wojdylak 2024-01-17 15:08:58 +01:00
parent 0a78b66500
commit f0446fe5d2
No known key found for this signature in database
GPG key ID: 7509E560A6821ABE
7 changed files with 341 additions and 239 deletions

View file

@ -13,144 +13,18 @@ declare(strict_types=1);
namespace Sylius\Bundle\UserBundle\Command;
use Doctrine\Persistence\ObjectManager;
use Sylius\Component\User\Model\UserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use SyliusLabs\Polyfill\Symfony\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Webmozart\Assert\Assert;
trigger_deprecation(
'sylius/user-bundle',
'1.13',
'The "%s" class is deprecated and will be removed in Sylius 2.0. Use "%s" instead.',
AbstractRoleCommand::class,
\Sylius\Bundle\UserBundle\Console\Command\AbstractRoleCommand::class,
);
abstract class AbstractRoleCommand extends ContainerAwareCommand
{
protected function interact(InputInterface $input, OutputInterface $output): void
class_exists(\Sylius\Bundle\UserBundle\Console\Command\AbstractRoleCommand::class);
if (false) {
abstract class AbstractRoleCommand
{
// User types configured in the Bundle
$availableUserTypes = $this->getAvailableUserTypes();
if (empty($availableUserTypes)) {
throw new \Exception(sprintf('At least one user type should implement %s', UserInterface::class));
}
$helper = $this->getHelper('question');
Assert::isInstanceOf($helper, QuestionHelper::class);
if (!$input->getOption('user-type')) {
// Do not ask if there's only 1 user type configured
if (count($availableUserTypes) === 1) {
$input->setOption('user-type', $availableUserTypes[0]);
} else {
$question = new ChoiceQuestion('Please enter the user type:', $availableUserTypes, 1);
$question->setErrorMessage('Choice %s is invalid.');
$userType = $helper->ask($input, $output, $question);
$input->setOption('user-type', $userType);
}
}
if (!$input->getArgument('email')) {
$question = new Question('Please enter an email:');
$question->setValidator(function (?string $email) {
if (!filter_var($email, \FILTER_VALIDATE_EMAIL)) {
throw new \RuntimeException('The email you entered is invalid.');
}
return $email;
});
$email = $helper->ask($input, $output, $question);
$input->setArgument('email', $email);
}
if (!$input->getArgument('roles')) {
$question = new Question('Please enter user\'s roles (separated by space):');
$question->setValidator(function (?string $roles) {
if ('' === $roles) {
throw new \RuntimeException('The value cannot be blank.');
}
return $roles;
});
$roles = $helper->ask($input, $output, $question);
if (!empty($roles)) {
$input->setArgument('roles', explode(' ', $roles));
}
}
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$email = $input->getArgument('email');
$securityRoles = $input->getArgument('roles');
$superAdmin = $input->getOption('super-admin');
$userType = $input->getOption('user-type');
if ($superAdmin) {
$securityRoles[] = 'ROLE_ADMINISTRATION_ACCESS';
}
/** @var UserInterface $user */
$user = $this->findUserByEmail($email, $userType);
$this->executeRoleCommand($input, $output, $user, $securityRoles);
return 0;
}
/**
* @throws \InvalidArgumentException
*/
protected function findUserByEmail(string $email, string $userType): UserInterface
{
/** @var UserInterface|null $user */
$user = $this->getUserRepository($userType)->findOneByEmail($email);
if (null === $user) {
throw new \InvalidArgumentException(sprintf('Could not find user identified by email "%s"', $email));
}
return $user;
}
protected function getEntityManager(string $userType): ObjectManager
{
$class = $this->getUserModelClass($userType);
return $this->getContainer()->get('doctrine')->getManagerForClass($class);
}
protected function getUserRepository(string $userType): UserRepositoryInterface
{
$class = $this->getUserModelClass($userType);
$userRepository = $this->getEntityManager($userType)->getRepository($class);
Assert::isInstanceOf($userRepository, UserRepositoryInterface::class);
return $userRepository;
}
protected function getAvailableUserTypes(): array
{
$config = $this->getContainer()->getParameter('sylius.user.users');
// Keep only users types which implement \Sylius\Component\User\Model\UserInterface
$userTypes = array_filter($config, fn (array $userTypeConfig): bool => isset($userTypeConfig['user']['classes']['model']) && is_a($userTypeConfig['user']['classes']['model'], UserInterface::class, true));
return array_keys($userTypes);
}
/**
* @throws \InvalidArgumentException
*/
protected function getUserModelClass(string $userType): string
{
$config = (array) $this->getContainer()->getParameter('sylius.user.users');
if (empty($config[$userType]['user']['classes']['model'])) {
throw new \InvalidArgumentException(sprintf('User type %s misconfigured.', $userType));
}
return $config[$userType]['user']['classes']['model'];
}
abstract protected function executeRoleCommand(InputInterface $input, OutputInterface $output, UserInterface $user, array $securityRoles): void;
}

View file

@ -13,58 +13,18 @@ declare(strict_types=1);
namespace Sylius\Bundle\UserBundle\Command;
use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
trigger_deprecation(
'sylius/user-bundle',
'1.13',
'The "%s" class is deprecated and will be removed in Sylius 2.0. Use "%s" instead.',
DemoteUserCommand::class,
\Sylius\Bundle\UserBundle\Console\Command\DemoteUserCommand::class,
);
class DemoteUserCommand extends AbstractRoleCommand
{
protected static $defaultName = 'sylius:user:demote';
class_exists(\Sylius\Bundle\UserBundle\Console\Command\DemoteUserCommand::class);
protected function configure(): void
if (false) {
class DemoteUserCommand
{
$this
->setDescription('Demotes a user by removing a role.')
->setDefinition([
new InputArgument('email', InputArgument::REQUIRED, 'Email'),
new InputArgument('roles', InputArgument::IS_ARRAY, 'Security roles'),
new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Unset the user as super admin'),
new InputOption('user-type', null, InputOption::VALUE_REQUIRED, 'Use shop or admin user type'),
])
->setHelp(
<<<EOT
The <info>sylius:user:demote</info> command demotes a user by removing security roles
<info>php app/console sylius:user:demote matthieu@email.com</info>
EOT
)
;
}
protected function executeRoleCommand(InputInterface $input, OutputInterface $output, UserInterface $user, array $securityRoles): void
{
$error = false;
$successMessages = [];
foreach ($securityRoles as $securityRole) {
if (!$user->hasRole($securityRole)) {
$output->writeln(sprintf('<error>User "%s" doesn\'t have "%s" Security role.</error>', $user->getEmail(), $securityRole));
$error = true;
continue;
}
$user->removeRole($securityRole);
$successMessages[] = sprintf('Security role <comment>%s</comment> has been removed from user <comment>%s</comment>', $securityRole, $user->getEmail());
}
if (!$error) {
$output->writeln($successMessages);
$this->getEntityManager($input->getOption('user-type'))->flush();
} else {
$output->writeln(sprintf('<error>No roles removed from User "%s".</error>', $user->getEmail()));
}
}
}

View file

@ -13,58 +13,18 @@ declare(strict_types=1);
namespace Sylius\Bundle\UserBundle\Command;
use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
trigger_deprecation(
'sylius/user-bundle',
'1.13',
'The "%s" class is deprecated and will be removed in Sylius 2.0. Use "%s" instead.',
PromoteUserCommand::class,
\Sylius\Bundle\UserBundle\Console\Command\PromoteUserCommand::class,
);
class PromoteUserCommand extends AbstractRoleCommand
{
protected static $defaultName = 'sylius:user:promote';
class_exists(\Sylius\Bundle\UserBundle\Console\Command\PromoteUserCommand::class);
protected function configure(): void
if (false) {
class PromoteUserCommand
{
$this
->setDescription('Promotes a user by adding roles.')
->setDefinition([
new InputArgument('email', InputArgument::REQUIRED, 'Email'),
new InputArgument('roles', InputArgument::IS_ARRAY, 'Security roles'),
new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Set the user as a super admin'),
new InputOption('user-type', null, InputOption::VALUE_REQUIRED, 'User type'),
])
->setHelp(
<<<EOT
The <info>sylius:user:promote</info> command promotes a user by adding security roles
<info>php app/console sylius:user:promote matthieu@email.com</info>
EOT
)
;
}
protected function executeRoleCommand(InputInterface $input, OutputInterface $output, UserInterface $user, array $securityRoles): void
{
$error = false;
$successMessages = [];
foreach ($securityRoles as $securityRole) {
if ($user->hasRole($securityRole)) {
$output->writeln(sprintf('<error>User "%s" already has "%s" security role.</error>', $user->getEmail(), $securityRole));
$error = true;
continue;
}
$user->addRole($securityRole);
$successMessages[] = sprintf('Security role <comment>%s</comment> has been added to user <comment>%s</comment>', $securityRole, $user->getEmail());
}
if (!$error) {
$output->writeln($successMessages);
$this->getEntityManager($input->getOption('user-type'))->flush();
} else {
$output->writeln(sprintf('<error>No roles added to User "%s".</error>', $user->getEmail()));
}
}
}

View file

@ -0,0 +1,160 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\UserBundle\Console\Command;
use Doctrine\Persistence\ObjectManager;
use Sylius\Component\User\Model\UserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use SyliusLabs\Polyfill\Symfony\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Webmozart\Assert\Assert;
abstract class AbstractRoleCommand extends ContainerAwareCommand
{
protected function interact(InputInterface $input, OutputInterface $output): void
{
// User types configured in the Bundle
$availableUserTypes = $this->getAvailableUserTypes();
if (empty($availableUserTypes)) {
throw new \Exception(sprintf('At least one user type should implement %s', UserInterface::class));
}
$helper = $this->getHelper('question');
Assert::isInstanceOf($helper, QuestionHelper::class);
if (!$input->getOption('user-type')) {
// Do not ask if there's only 1 user type configured
if (count($availableUserTypes) === 1) {
$input->setOption('user-type', $availableUserTypes[0]);
} else {
$question = new ChoiceQuestion('Please enter the user type:', $availableUserTypes, 1);
$question->setErrorMessage('Choice %s is invalid.');
$userType = $helper->ask($input, $output, $question);
$input->setOption('user-type', $userType);
}
}
if (!$input->getArgument('email')) {
$question = new Question('Please enter an email:');
$question->setValidator(function (?string $email) {
if (!filter_var($email, \FILTER_VALIDATE_EMAIL)) {
throw new \RuntimeException('The email you entered is invalid.');
}
return $email;
});
$email = $helper->ask($input, $output, $question);
$input->setArgument('email', $email);
}
if (!$input->getArgument('roles')) {
$question = new Question('Please enter user\'s roles (separated by space):');
$question->setValidator(function (?string $roles) {
if ('' === $roles) {
throw new \RuntimeException('The value cannot be blank.');
}
return $roles;
});
$roles = $helper->ask($input, $output, $question);
if (!empty($roles)) {
$input->setArgument('roles', explode(' ', $roles));
}
}
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$email = $input->getArgument('email');
$securityRoles = $input->getArgument('roles');
$superAdmin = $input->getOption('super-admin');
$userType = $input->getOption('user-type');
if ($superAdmin) {
$securityRoles[] = 'ROLE_ADMINISTRATION_ACCESS';
}
/** @var UserInterface $user */
$user = $this->findUserByEmail($email, $userType);
$this->executeRoleCommand($input, $output, $user, $securityRoles);
return 0;
}
/**
* @throws \InvalidArgumentException
*/
protected function findUserByEmail(string $email, string $userType): UserInterface
{
/** @var UserInterface|null $user */
$user = $this->getUserRepository($userType)->findOneByEmail($email);
if (null === $user) {
throw new \InvalidArgumentException(sprintf('Could not find user identified by email "%s"', $email));
}
return $user;
}
protected function getEntityManager(string $userType): ObjectManager
{
$class = $this->getUserModelClass($userType);
return $this->getContainer()->get('doctrine')->getManagerForClass($class);
}
protected function getUserRepository(string $userType): UserRepositoryInterface
{
$class = $this->getUserModelClass($userType);
$userRepository = $this->getEntityManager($userType)->getRepository($class);
Assert::isInstanceOf($userRepository, UserRepositoryInterface::class);
return $userRepository;
}
/** @return array<string> */
protected function getAvailableUserTypes(): array
{
$config = $this->getContainer()->getParameter('sylius.user.users');
// Keep only users types which implement \Sylius\Component\User\Model\UserInterface
$userTypes = array_filter($config, fn (array $userTypeConfig): bool => isset($userTypeConfig['user']['classes']['model']) && is_a($userTypeConfig['user']['classes']['model'], UserInterface::class, true));
return array_keys($userTypes);
}
/**
* @throws \InvalidArgumentException
*/
protected function getUserModelClass(string $userType): string
{
$config = (array) $this->getContainer()->getParameter('sylius.user.users');
if (empty($config[$userType]['user']['classes']['model'])) {
throw new \InvalidArgumentException(sprintf('User type %s misconfigured.', $userType));
}
return $config[$userType]['user']['classes']['model'];
}
/** @param array<array-key, string> $securityRoles */
abstract protected function executeRoleCommand(InputInterface $input, OutputInterface $output, UserInterface $user, array $securityRoles): void;
}
class_alias(AbstractRoleCommand::class, \Sylius\Bundle\UserBundle\Command\AbstractRoleCommand::class);

View file

@ -0,0 +1,72 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\UserBundle\Console\Command;
use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class DemoteUserCommand extends AbstractRoleCommand
{
protected static $defaultName = 'sylius:user:demote';
protected function configure(): void
{
$this
->setDescription('Demotes a user by removing a role.')
->setDefinition([
new InputArgument('email', InputArgument::REQUIRED, 'Email'),
new InputArgument('roles', InputArgument::IS_ARRAY, 'Security roles'),
new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Unset the user as super admin'),
new InputOption('user-type', null, InputOption::VALUE_REQUIRED, 'Use shop or admin user type'),
])
->setHelp(
<<<EOT
The <info>sylius:user:demote</info> command demotes a user by removing security roles
<info>php app/console sylius:user:demote matthieu@email.com</info>
EOT
)
;
}
protected function executeRoleCommand(InputInterface $input, OutputInterface $output, UserInterface $user, array $securityRoles): void
{
$error = false;
$successMessages = [];
foreach ($securityRoles as $securityRole) {
if (!$user->hasRole($securityRole)) {
$output->writeln(sprintf('<error>User "%s" doesn\'t have "%s" Security role.</error>', $user->getEmail(), $securityRole));
$error = true;
continue;
}
$user->removeRole($securityRole);
$successMessages[] = sprintf('Security role <comment>%s</comment> has been removed from user <comment>%s</comment>', $securityRole, $user->getEmail());
}
if (!$error) {
$output->writeln($successMessages);
$this->getEntityManager($input->getOption('user-type'))->flush();
} else {
$output->writeln(sprintf('<error>No roles removed from User "%s".</error>', $user->getEmail()));
}
}
}
class_alias(DemoteUserCommand::class, \Sylius\Bundle\UserBundle\Command\DemoteUserCommand::class);

View file

@ -0,0 +1,72 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\UserBundle\Console\Command;
use Sylius\Component\User\Model\UserInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class PromoteUserCommand extends AbstractRoleCommand
{
protected static $defaultName = 'sylius:user:promote';
protected function configure(): void
{
$this
->setDescription('Promotes a user by adding roles.')
->setDefinition([
new InputArgument('email', InputArgument::REQUIRED, 'Email'),
new InputArgument('roles', InputArgument::IS_ARRAY, 'Security roles'),
new InputOption('super-admin', null, InputOption::VALUE_NONE, 'Set the user as a super admin'),
new InputOption('user-type', null, InputOption::VALUE_REQUIRED, 'User type'),
])
->setHelp(
<<<EOT
The <info>sylius:user:promote</info> command promotes a user by adding security roles
<info>php app/console sylius:user:promote matthieu@email.com</info>
EOT
)
;
}
protected function executeRoleCommand(InputInterface $input, OutputInterface $output, UserInterface $user, array $securityRoles): void
{
$error = false;
$successMessages = [];
foreach ($securityRoles as $securityRole) {
if ($user->hasRole($securityRole)) {
$output->writeln(sprintf('<error>User "%s" already has "%s" security role.</error>', $user->getEmail(), $securityRole));
$error = true;
continue;
}
$user->addRole($securityRole);
$successMessages[] = sprintf('Security role <comment>%s</comment> has been added to user <comment>%s</comment>', $securityRole, $user->getEmail());
}
if (!$error) {
$output->writeln($successMessages);
$this->getEntityManager($input->getOption('user-type'))->flush();
} else {
$output->writeln(sprintf('<error>No roles added to User "%s".</error>', $user->getEmail()));
}
}
}
class_alias(PromoteUserCommand::class, \Sylius\Bundle\UserBundle\Command\PromoteUserCommand::class);

View file

@ -27,14 +27,18 @@
<services>
<defaults public="true" />
<service id="Sylius\Bundle\UserBundle\Command\DemoteUserCommand">
<service id="Sylius\Bundle\UserBundle\Console\Command\DemoteUserCommand">
<tag name="console.command" />
</service>
<service id="Sylius\Bundle\UserBundle\Command\PromoteUserCommand">
<service id="Sylius\Bundle\UserBundle\Command\DemoteUserCommand" alias="Sylius\Bundle\UserBundle\Console\Command\DemoteUserCommand" />
<service id="Sylius\Bundle\UserBundle\Console\Command\PromoteUserCommand">
<tag name="console.command" />
</service>
<service id="Sylius\Bundle\UserBundle\Command\PromoteUserCommand" alias="Sylius\Bundle\UserBundle\Console\Command\PromoteUserCommand" />
<service id="sylius.authentication.success_handler" class="Sylius\Bundle\UserBundle\Authentication\AuthenticationSuccessHandler" parent="security.authentication.success_handler" public="false" />
<service id="sylius.authentication.failure_handler" class="Sylius\Bundle\UserBundle\Authentication\AuthenticationFailureHandler" parent="security.authentication.failure_handler" public="false" />