Skip to content

Commit

Permalink
feature #250 Adding a make:user command (weaverryan)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 1.0-dev branch (closes #250).

Discussion
----------

Adding a make:user command

Hi guys!

My latest experiment πŸ‘¨πŸ»β€πŸ”¬!

**User is an Entity**
<img width="1277" alt="screen shot 2018-08-28 at 2 16 20 pm" src="https://user-images.githubusercontent.com/121003/44742077-faa57d80-aacc-11e8-8f73-acce0b669c7a.png">

**User is a Model Class**
<img width="1278" alt="screen shot 2018-08-28 at 2 11 38 pm" src="https://user-images.githubusercontent.com/121003/44742324-98994800-aacd-11e8-8ddd-8e52310dc61a.png">

This walks people through some of the most confusing parts of starting with Symfony's security so that they can get straight to writing authenticators.

A) It can generate an **entity or non-entity** `User` class
B) It sets up your **`User::getUsername()` method correctly**, which can be confusing... because often you don't have a username (e.g. you have an email)
C) It correctly **fills in `getSalt()` and `getPassword()`** based on whether or not your app actually *needs* to check passwords
D) It **updates** the `providers` and `encoders` sections in **`security.yaml` file without losing formatting or comments**. If this process fails (it's not perfect), the command will tell you exactly what YAML to update. It defaults to using `argon2i` when the system supports it.

Example generated `User` class source:  `tests/Security/fixtures/expected/*`
Example generated `security.yaml` source:  `tests/Security/yaml_fixtures/expected_user_class/`*

I'd love to have feedback from people trying it. You should be able to switch to the `dev-make-user` branch of this repository to get it.

Cheers!

Commits
-------

c5fb5df more phpcs fixing
603a815 reducing length of unique field to avoid index length problems
f6e273a Not requiring \in_array() style on generated code
23116fa bumping MySQL sever_version to 5.6 to fix Travis & JSON fields
3a40c86 updating to latest phpcs
c44b1d5 Getting info about cs problems
93a846d Adding make:user command
  • Loading branch information
weaverryan committed Aug 30, 2018
2 parents 20cdf7f + c5fb5df commit 7a12438
Show file tree
Hide file tree
Showing 98 changed files with 4,278 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ before_script:

script:
- ./vendor/bin/simple-phpunit
- ./vendor/bin/php-cs-fixer fix --dry-run
- ./vendor/bin/php-cs-fixer fix --dry-run --diff
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"doctrine/orm": "^2.3",
"friendsofphp/php-cs-fixer": "^2.8",
"symfony/phpunit-bridge": "^3.4|^4.0",
"symfony/process": "^3.4|^4.0"
"symfony/process": "^3.4|^4.0",
"symfony/yaml": "^3.4|^4.0"
},
"config": {
"preferred-install": "dist",
Expand Down
4 changes: 4 additions & 0 deletions src/Command/MakerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ protected function initialize(InputInterface $input, OutputInterface $output)
$dependencies = new DependencyBuilder();
$this->maker->configureDependencies($dependencies, $input);

if (!$dependencies->isPhpVersionSatisfied()) {
throw new RuntimeCommandException('The make:entity command requires that you use PHP 7.1 or higher.');
}

if ($missingPackagesMessage = $dependencies->getMissingPackagesMessage($this->getName())) {
throw new RuntimeCommandException($missingPackagesMessage);
}
Expand Down
23 changes: 19 additions & 4 deletions src/DependencyBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ final class DependencyBuilder
private $dependencies = [];
private $devDependencies = [];

private $minimumPHPVersion = 70000;

/**
* Add a dependency that will be reported if the given class is missing.
*
Expand All @@ -40,6 +42,11 @@ public function addClassDependency(string $class, string $package, bool $require
}
}

public function requirePHP71()
{
$this->minimumPHPVersion = 70100;
}

/**
* @internal
*/
Expand Down Expand Up @@ -75,7 +82,7 @@ public function getAllRequiredDevDependencies(): array
/**
* @internal
*/
public function getMissingPackagesMessage(string $commandName): string
public function getMissingPackagesMessage(string $commandName, $message = null): string
{
$packages = $this->getMissingDependencies();
$packagesDev = $this->getMissingDevDependencies();
Expand All @@ -87,9 +94,9 @@ public function getMissingPackagesMessage(string $commandName): string
$packagesCount = \count($packages) + \count($packagesDev);

$message = sprintf(
"Missing package%s: to use the %s command, run:\n",
"Missing package%s: %s, run:\n",
$packagesCount > 1 ? 's' : '',
$commandName
$message ? $message : sprintf('to use the %s command', $commandName)
);

if (!empty($packages)) {
Expand All @@ -103,6 +110,14 @@ public function getMissingPackagesMessage(string $commandName): string
return $message;
}

/**
* @internal
*/
public function isPhpVersionSatisfied(): bool
{
return \PHP_VERSION_ID >= $this->minimumPHPVersion;
}

private function getRequiredDependencyNames(array $dependencies): array
{
$packages = [];
Expand All @@ -121,7 +136,7 @@ private function calculateMissingDependencies(array $dependencies): array
$missingPackages = [];
$missingOptionalPackages = [];
foreach ($dependencies as $package) {
if (class_exists($package['class'])) {
if (class_exists($package['class']) || interface_exists($package['class'])) {
continue;
}
if (true === $package['required']) {
Expand Down
59 changes: 59 additions & 0 deletions src/Doctrine/EntityClassGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/*
* This file is part of the Symfony MakerBundle package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\MakerBundle\Doctrine;

use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;

/**
* @internal
*/
final class EntityClassGenerator
{
private $generator;

public function __construct(Generator $generator)
{
$this->generator = $generator;
}

public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource): string
{
$repoClassDetails = $this->generator->createClassNameDetails(
$entityClassDetails->getRelativeName(),
'Repository\\',
'Repository'
);

$entityPath = $this->generator->generateClass(
$entityClassDetails->getFullName(),
'doctrine/Entity.tpl.php',
[
'repository_full_class_name' => $repoClassDetails->getFullName(),
'api_resource' => $apiResource,
]
);

$entityAlias = strtolower($entityClassDetails->getShortName()[0]);
$this->generator->generateClass(
$repoClassDetails->getFullName(),
'doctrine/Repository.tpl.php',
[
'entity_full_class_name' => $entityClassDetails->getFullName(),
'entity_class_name' => $entityClassDetails->getShortName(),
'entity_alias' => $entityAlias,
]
);

return $entityPath;
}
}
44 changes: 44 additions & 0 deletions src/Doctrine/ORMDependencyBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of the Symfony MakerBundle package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\MakerBundle\Doctrine;

use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\ORM\Mapping\Column;
use Symfony\Bundle\MakerBundle\DependencyBuilder;

/**
* @internal
*/
final class ORMDependencyBuilder
{
/**
* Central method to add dependencies needed for Doctrine ORM.
*
* @param DependencyBuilder $dependencies
*/
public static function buildDependencies(DependencyBuilder $dependencies)
{
$classes = [
// guarantee DoctrineBundle
DoctrineBundle::class,
// guarantee ORM
Column::class,
];

foreach ($classes as $class) {
$dependencies->addClassDependency(
$class,
'orm'
);
}
}
}
23 changes: 22 additions & 1 deletion src/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ public function __construct(FileManager $fileManager, string $namespacePrefix)

/**
* Generate a new file for a class from a template.
*
* @param string $className The fully-qualified class name
* @param string $templateName Template name in Resources/skeleton to use
* @param array $variables Array of variables to pass to the template
*
* @return string The path where the file will be created
*
* @throws \Exception
*/
public function generateClass(string $className, string $templateName, array $variables): string
public function generateClass(string $className, string $templateName, array $variables = []): string
{
$targetPath = $this->fileManager->getRelativePathForFutureClass($className);

Expand Down Expand Up @@ -69,6 +77,13 @@ public function generateFile(string $targetPath, string $templateName, array $va
$this->addOperation($targetPath, $templateName, $variables);
}

public function dumpFile(string $targetPath, string $contents)
{
$this->pendingOperations[$targetPath] = [
'contents' => $contents,
];
}

/**
* Creates a helper object to get data about a class name.
*
Expand Down Expand Up @@ -157,6 +172,12 @@ public function hasPendingOperations(): bool
public function writeChanges()
{
foreach ($this->pendingOperations as $targetPath => $templateData) {
if (isset($templateData['contents'])) {
$this->fileManager->dumpFile($targetPath, $templateData['contents']);

continue;
}

$templatePath = $templateData['template'];
$parameters = $templateData['variables'];

Expand Down
52 changes: 10 additions & 42 deletions src/Maker/MakeEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
namespace Symfony\Bundle\MakerBundle\Maker;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;
use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;
Expand Down Expand Up @@ -134,10 +134,6 @@ class_exists(ApiResource::class) &&

public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
{
if (\PHP_VERSION_ID < 70100) {
throw new RuntimeCommandException('The make:entity command requires that you use PHP 7.1 or higher.');
}

$overwrite = $input->getOption('overwrite');

// the regenerate option has entirely custom behavior
Expand All @@ -153,32 +149,12 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
'Entity\\'
);

$repositoryClassDetails = $generator->createClassNameDetails(
$entityClassDetails->getRelativeName(),
'Repository\\',
'Repository'
);

$classExists = class_exists($entityClassDetails->getFullName());
if (!$classExists) {
$entityPath = $generator->generateClass(
$entityClassDetails->getFullName(),
'doctrine/Entity.tpl.php',
[
'repository_full_class_name' => $repositoryClassDetails->getFullName(),
'api_resource' => $input->getOption('api-resource'),
]
);

$entityAlias = strtolower($entityClassDetails->getShortName()[0]);
$generator->generateClass(
$repositoryClassDetails->getFullName(),
'doctrine/Repository.tpl.php',
[
'entity_full_class_name' => $entityClassDetails->getFullName(),
'entity_class_name' => $entityClassDetails->getShortName(),
'entity_alias' => $entityAlias,
]
$entityClassGenerator = new EntityClassGenerator($generator);
$entityPath = $entityClassGenerator->generateEntityClass(
$entityClassDetails,
$input->getOption('api-resource')
);

$generator->writeChanges();
Expand Down Expand Up @@ -296,24 +272,16 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen

public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null)
{
$dependencies->requirePHP71();

if (null !== $input && $input->getOption('api-resource')) {
$dependencies->addClassDependency(
ApiResource::class,
'api'
);
}

// guarantee DoctrineBundle
$dependencies->addClassDependency(
DoctrineBundle::class,
'orm'
);

// guarantee ORM
$dependencies->addClassDependency(
Column::class,
'orm'
);
ORMDependencyBuilder::buildDependencies($dependencies);
}

private function askForNextField(ConsoleStyle $io, array $fields, string $entityClass, bool $isFirstField)
Expand Down Expand Up @@ -827,7 +795,7 @@ private function getPropertyNames(string $class): array
private function doesEntityUseAnnotationMapping(string $className): bool
{
if (!class_exists($className)) {
$otherClassMetadatas = $this->doctrineHelper->getMetadata(Str::getNamespace($className) . '\\', true);
$otherClassMetadatas = $this->doctrineHelper->getMetadata(Str::getNamespace($className).'\\', true);

// if we have no metadata, we should assume this is the first class being mapped
if (empty($otherClassMetadatas)) {
Expand Down
Loading

0 comments on commit 7a12438

Please sign in to comment.