Skip to content

Commit 7a12438

Browse files
committed
feature #250 Adding a make:user command (weaverryan)
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
2 parents 20cdf7f + c5fb5df commit 7a12438

File tree

98 files changed

+4278
-82
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+4278
-82
lines changed

β€Ž.travis.ymlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ before_script:
2929

3030
script:
3131
- ./vendor/bin/simple-phpunit
32-
- ./vendor/bin/php-cs-fixer fix --dry-run
32+
- ./vendor/bin/php-cs-fixer fix --dry-run --diff

β€Žcomposer.jsonβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"doctrine/orm": "^2.3",
3030
"friendsofphp/php-cs-fixer": "^2.8",
3131
"symfony/phpunit-bridge": "^3.4|^4.0",
32-
"symfony/process": "^3.4|^4.0"
32+
"symfony/process": "^3.4|^4.0",
33+
"symfony/yaml": "^3.4|^4.0"
3334
},
3435
"config": {
3536
"preferred-install": "dist",

β€Žsrc/Command/MakerCommand.phpβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ protected function initialize(InputInterface $input, OutputInterface $output)
6464
$dependencies = new DependencyBuilder();
6565
$this->maker->configureDependencies($dependencies, $input);
6666

67+
if (!$dependencies->isPhpVersionSatisfied()) {
68+
throw new RuntimeCommandException('The make:entity command requires that you use PHP 7.1 or higher.');
69+
}
70+
6771
if ($missingPackagesMessage = $dependencies->getMissingPackagesMessage($this->getName())) {
6872
throw new RuntimeCommandException($missingPackagesMessage);
6973
}

β€Žsrc/DependencyBuilder.phpβ€Ž

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ final class DependencyBuilder
1616
private $dependencies = [];
1717
private $devDependencies = [];
1818

19+
private $minimumPHPVersion = 70000;
20+
1921
/**
2022
* Add a dependency that will be reported if the given class is missing.
2123
*
@@ -40,6 +42,11 @@ public function addClassDependency(string $class, string $package, bool $require
4042
}
4143
}
4244

45+
public function requirePHP71()
46+
{
47+
$this->minimumPHPVersion = 70100;
48+
}
49+
4350
/**
4451
* @internal
4552
*/
@@ -75,7 +82,7 @@ public function getAllRequiredDevDependencies(): array
7582
/**
7683
* @internal
7784
*/
78-
public function getMissingPackagesMessage(string $commandName): string
85+
public function getMissingPackagesMessage(string $commandName, $message = null): string
7986
{
8087
$packages = $this->getMissingDependencies();
8188
$packagesDev = $this->getMissingDevDependencies();
@@ -87,9 +94,9 @@ public function getMissingPackagesMessage(string $commandName): string
8794
$packagesCount = \count($packages) + \count($packagesDev);
8895

8996
$message = sprintf(
90-
"Missing package%s: to use the %s command, run:\n",
97+
"Missing package%s: %s, run:\n",
9198
$packagesCount > 1 ? 's' : '',
92-
$commandName
99+
$message ? $message : sprintf('to use the %s command', $commandName)
93100
);
94101

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

113+
/**
114+
* @internal
115+
*/
116+
public function isPhpVersionSatisfied(): bool
117+
{
118+
return \PHP_VERSION_ID >= $this->minimumPHPVersion;
119+
}
120+
106121
private function getRequiredDependencyNames(array $dependencies): array
107122
{
108123
$packages = [];
@@ -121,7 +136,7 @@ private function calculateMissingDependencies(array $dependencies): array
121136
$missingPackages = [];
122137
$missingOptionalPackages = [];
123138
foreach ($dependencies as $package) {
124-
if (class_exists($package['class'])) {
139+
if (class_exists($package['class']) || interface_exists($package['class'])) {
125140
continue;
126141
}
127142
if (true === $package['required']) {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Doctrine;
13+
14+
use Symfony\Bundle\MakerBundle\Generator;
15+
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
16+
17+
/**
18+
* @internal
19+
*/
20+
final class EntityClassGenerator
21+
{
22+
private $generator;
23+
24+
public function __construct(Generator $generator)
25+
{
26+
$this->generator = $generator;
27+
}
28+
29+
public function generateEntityClass(ClassNameDetails $entityClassDetails, bool $apiResource): string
30+
{
31+
$repoClassDetails = $this->generator->createClassNameDetails(
32+
$entityClassDetails->getRelativeName(),
33+
'Repository\\',
34+
'Repository'
35+
);
36+
37+
$entityPath = $this->generator->generateClass(
38+
$entityClassDetails->getFullName(),
39+
'doctrine/Entity.tpl.php',
40+
[
41+
'repository_full_class_name' => $repoClassDetails->getFullName(),
42+
'api_resource' => $apiResource,
43+
]
44+
);
45+
46+
$entityAlias = strtolower($entityClassDetails->getShortName()[0]);
47+
$this->generator->generateClass(
48+
$repoClassDetails->getFullName(),
49+
'doctrine/Repository.tpl.php',
50+
[
51+
'entity_full_class_name' => $entityClassDetails->getFullName(),
52+
'entity_class_name' => $entityClassDetails->getShortName(),
53+
'entity_alias' => $entityAlias,
54+
]
55+
);
56+
57+
return $entityPath;
58+
}
59+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Doctrine;
13+
14+
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
15+
use Doctrine\ORM\Mapping\Column;
16+
use Symfony\Bundle\MakerBundle\DependencyBuilder;
17+
18+
/**
19+
* @internal
20+
*/
21+
final class ORMDependencyBuilder
22+
{
23+
/**
24+
* Central method to add dependencies needed for Doctrine ORM.
25+
*
26+
* @param DependencyBuilder $dependencies
27+
*/
28+
public static function buildDependencies(DependencyBuilder $dependencies)
29+
{
30+
$classes = [
31+
// guarantee DoctrineBundle
32+
DoctrineBundle::class,
33+
// guarantee ORM
34+
Column::class,
35+
];
36+
37+
foreach ($classes as $class) {
38+
$dependencies->addClassDependency(
39+
$class,
40+
'orm'
41+
);
42+
}
43+
}
44+
}

β€Žsrc/Generator.phpβ€Ž

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,16 @@ public function __construct(FileManager $fileManager, string $namespacePrefix)
3434

3535
/**
3636
* Generate a new file for a class from a template.
37+
*
38+
* @param string $className The fully-qualified class name
39+
* @param string $templateName Template name in Resources/skeleton to use
40+
* @param array $variables Array of variables to pass to the template
41+
*
42+
* @return string The path where the file will be created
43+
*
44+
* @throws \Exception
3745
*/
38-
public function generateClass(string $className, string $templateName, array $variables): string
46+
public function generateClass(string $className, string $templateName, array $variables = []): string
3947
{
4048
$targetPath = $this->fileManager->getRelativePathForFutureClass($className);
4149

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

80+
public function dumpFile(string $targetPath, string $contents)
81+
{
82+
$this->pendingOperations[$targetPath] = [
83+
'contents' => $contents,
84+
];
85+
}
86+
7287
/**
7388
* Creates a helper object to get data about a class name.
7489
*
@@ -157,6 +172,12 @@ public function hasPendingOperations(): bool
157172
public function writeChanges()
158173
{
159174
foreach ($this->pendingOperations as $targetPath => $templateData) {
175+
if (isset($templateData['contents'])) {
176+
$this->fileManager->dumpFile($targetPath, $templateData['contents']);
177+
178+
continue;
179+
}
180+
160181
$templatePath = $templateData['template'];
161182
$parameters = $templateData['variables'];
162183

β€Žsrc/Maker/MakeEntity.phpβ€Ž

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
namespace Symfony\Bundle\MakerBundle\Maker;
1313

1414
use ApiPlatform\Core\Annotation\ApiResource;
15-
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
1615
use Doctrine\DBAL\Types\Type;
17-
use Doctrine\ORM\Mapping\Column;
1816
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
1917
use Symfony\Bundle\MakerBundle\ConsoleStyle;
2018
use Symfony\Bundle\MakerBundle\DependencyBuilder;
2119
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
20+
use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;
21+
use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;
2222
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
2323
use Symfony\Bundle\MakerBundle\Generator;
2424
use Symfony\Bundle\MakerBundle\InputAwareMakerInterface;
@@ -134,10 +134,6 @@ class_exists(ApiResource::class) &&
134134

135135
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
136136
{
137-
if (\PHP_VERSION_ID < 70100) {
138-
throw new RuntimeCommandException('The make:entity command requires that you use PHP 7.1 or higher.');
139-
}
140-
141137
$overwrite = $input->getOption('overwrite');
142138

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

156-
$repositoryClassDetails = $generator->createClassNameDetails(
157-
$entityClassDetails->getRelativeName(),
158-
'Repository\\',
159-
'Repository'
160-
);
161-
162152
$classExists = class_exists($entityClassDetails->getFullName());
163153
if (!$classExists) {
164-
$entityPath = $generator->generateClass(
165-
$entityClassDetails->getFullName(),
166-
'doctrine/Entity.tpl.php',
167-
[
168-
'repository_full_class_name' => $repositoryClassDetails->getFullName(),
169-
'api_resource' => $input->getOption('api-resource'),
170-
]
171-
);
172-
173-
$entityAlias = strtolower($entityClassDetails->getShortName()[0]);
174-
$generator->generateClass(
175-
$repositoryClassDetails->getFullName(),
176-
'doctrine/Repository.tpl.php',
177-
[
178-
'entity_full_class_name' => $entityClassDetails->getFullName(),
179-
'entity_class_name' => $entityClassDetails->getShortName(),
180-
'entity_alias' => $entityAlias,
181-
]
154+
$entityClassGenerator = new EntityClassGenerator($generator);
155+
$entityPath = $entityClassGenerator->generateEntityClass(
156+
$entityClassDetails,
157+
$input->getOption('api-resource')
182158
);
183159

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

297273
public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null)
298274
{
275+
$dependencies->requirePHP71();
276+
299277
if (null !== $input && $input->getOption('api-resource')) {
300278
$dependencies->addClassDependency(
301279
ApiResource::class,
302280
'api'
303281
);
304282
}
305283

306-
// guarantee DoctrineBundle
307-
$dependencies->addClassDependency(
308-
DoctrineBundle::class,
309-
'orm'
310-
);
311-
312-
// guarantee ORM
313-
$dependencies->addClassDependency(
314-
Column::class,
315-
'orm'
316-
);
284+
ORMDependencyBuilder::buildDependencies($dependencies);
317285
}
318286

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

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

0 commit comments

Comments
Β (0)