Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #14 Added a new command to create users (javiereguiluz)
This PR was squashed before being merged into the master branch (closes #14). Discussion ---------- Added a new command to create users The command it's almost finished. I'll add more help notes and the `interact()` method to show how to use it. Commits ------- d33a650 Added a new command to create users
- Loading branch information
Showing
4 changed files
with
313 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
<?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 AppBundle\Command; | ||
|
||
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
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\Question\Question; | ||
use Doctrine\Common\Persistence\ObjectManager; | ||
use AppBundle\Entity\User; | ||
|
||
/** | ||
* A command console 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 app/console app:add-user | ||
* | ||
* To output detailed information, increase the command verbosity: | ||
* $ php app/console app:add-user -vv | ||
* | ||
* See http://symfony.com/doc/current/cookbook/console/console_command.html | ||
* | ||
* @author Javier Eguiluz <javier.eguiluz@gmail.com> | ||
*/ | ||
class AddUserCommand extends ContainerAwareCommand | ||
{ | ||
const MAX_ATTEMPTS = 5; | ||
|
||
/** @var ObjectManager */ | ||
private $em; | ||
|
||
protected function configure() | ||
{ | ||
$this | ||
// a good practice is to use the 'app:' prefix to group all your custom application commands | ||
->setName('app:add-user') | ||
->setDescription('Creates users and stores them in the database') | ||
->setHelp($this->getCommandHelp()) | ||
// commands can optionally define arguments and/or options (mandatory and optional) | ||
// see http://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') | ||
->addOption('is-admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator') | ||
; | ||
} | ||
|
||
/** | ||
* This method is executed before initialize() and 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')) { | ||
return; | ||
} | ||
|
||
$output->writeln(''); | ||
$output->writeln('Add User Command Interactive Wizard'); | ||
$output->writeln('-----------------------------------'); | ||
|
||
$output->writeln(array( | ||
'', | ||
'If you prefer to not use this interactive wizard, provide the', | ||
'arguments required by this command as follows:', | ||
'', | ||
' $ php app/console app:add-user username password email@example.com', | ||
'', | ||
)); | ||
|
||
$output->writeln(array( | ||
'', | ||
'Now we\'ll ask you for the value of all the missing command arguments.', | ||
'', | ||
)); | ||
|
||
// See http://symfony.com/doc/current/components/console/helpers/questionhelper.html | ||
$console = $this->getHelper('question'); | ||
|
||
// Ask for the username if it's not defined | ||
if (null === $username = $input->getArgument('username')) { | ||
$question = new Question(' > <info>Username</>: '); | ||
$question->setValidator(function ($answer) { | ||
if (empty($answer)) { | ||
throw new \RuntimeException('The username cannot be empty'); | ||
} | ||
|
||
return $answer; | ||
}); | ||
$question->setMaxAttempts(self::MAX_ATTEMPTS); | ||
|
||
$username = $console->ask($input, $output, $question); | ||
$input->setArgument('username', $username); | ||
} else { | ||
$output->writeln(' > <info>Username</>: '.$username); | ||
} | ||
|
||
// Ask for the password if it's not defined | ||
if (null === $password = $input->getArgument('password')) { | ||
$question = new Question(' > <info>Password</> (your type will be hidden): '); | ||
$question->setValidator(array($this, 'passwordValidator')); | ||
$question->setHidden(true); | ||
$question->setMaxAttempts(self::MAX_ATTEMPTS); | ||
|
||
$password = $console->ask($input, $output, $question); | ||
$input->setArgument('password', $password); | ||
} else { | ||
$output->writeln(' > <info>Password</>: '.str_repeat('*', strlen($password))); | ||
} | ||
|
||
// Ask for the email if it's not defined | ||
if (null === $email = $input->getArgument('email')) { | ||
$question = new Question(' > <info>Email</>: '); | ||
$question->setValidator(array($this, 'emailValidator')); | ||
$question->setMaxAttempts(self::MAX_ATTEMPTS); | ||
|
||
$email = $console->ask($input, $output, $question); | ||
$input->setArgument('email', $email); | ||
} else { | ||
$output->writeln(' > <info>Email</>: '.$email); | ||
} | ||
} | ||
|
||
/** | ||
* This method is executed after the interact() and before the execute() | ||
* method. It's main purpose is to initialize the variables used in the rest | ||
* of the command methods. | ||
*/ | ||
protected function initialize(InputInterface $input, OutputInterface $output) | ||
{ | ||
$this->em = $this->getContainer()->get('doctrine')->getManager(); | ||
} | ||
|
||
/** | ||
* 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) | ||
{ | ||
$startTime = microtime(true); | ||
|
||
$username = $input->getArgument('username'); | ||
$plainPassword = $input->getArgument('password'); | ||
$email = $input->getArgument('email'); | ||
$isAdmin = $input->getOption('is-admin'); | ||
|
||
// first check if a user with the same username already exists | ||
$existingUser = $this->em->getRepository('AppBundle:User')->findOneBy(array('username' => $username)); | ||
|
||
if (null !== $existingUser) { | ||
throw new \RuntimeException(sprintf('There is already a user registered with the "%s" username.', $username)); | ||
} | ||
|
||
// create the user and encode its password | ||
$user = new User(); | ||
$user->setUsername($username); | ||
$user->setEmail($email); | ||
$user->setRoles(array($isAdmin ? 'ROLE_ADMIN' : 'ROLE_USER')); | ||
|
||
// See http://symfony.com/doc/current/book/security.html#security-encoding-password | ||
$encoder = $this->getContainer()->get('security.password_encoder'); | ||
$encodedPassword = $encoder->encodePassword($user, $plainPassword); | ||
$user->setPassword($encodedPassword); | ||
|
||
$this->em->persist($user); | ||
$this->em->flush($user); | ||
|
||
$output->writeln(''); | ||
$output->writeln(sprintf('[OK] %s was successfully created: %s (%s)', $isAdmin ? 'Administrator user' : 'User', $user->getUsername(), $user->getEmail())); | ||
|
||
if ($output->isVerbose()) { | ||
$finishTime = microtime(true); | ||
$elapsedTime = $finishTime - $startTime; | ||
|
||
$output->writeln(sprintf('[INFO] New user database id: %d / Elapsed time: %.2f ms', $user->getId(), $elapsedTime*1000)); | ||
} | ||
} | ||
|
||
/** | ||
* This internal method should be private, but it's declared as public to | ||
* maintain PHP 5.3 compatibility when using it in a callback. | ||
* | ||
* @internal | ||
*/ | ||
public function passwordValidator($plainPassword) | ||
{ | ||
if (empty($plainPassword)) { | ||
throw new \Exception('The password can not be empty'); | ||
} | ||
|
||
if (strlen(trim($plainPassword)) < 6) { | ||
throw new \Exception('The password must be at least 6 characters long'); | ||
} | ||
|
||
return $plainPassword; | ||
} | ||
|
||
/** | ||
* This internal method should be private, but it's declared as public to | ||
* maintain PHP 5.3 compatibility when using it in a callback. | ||
* | ||
* @internal | ||
*/ | ||
public function emailValidator($email) | ||
{ | ||
if (empty($email)) { | ||
throw new \Exception('The email can not be empty'); | ||
} | ||
|
||
if (false === strpos($email, '@')) { | ||
throw new \Exception('The email should look like a real email'); | ||
} | ||
|
||
return $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() | ||
{ | ||
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>--is-admin</comment> option: | ||
<info>php %command.full_name%</info> username password email <comment>--is-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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters