Skip to content

Commit f7ced83

Browse files
committed
feature #173 Add configuration of root namespace (Sergey Rabochiy, weaverryan)
This PR was merged into the 1.0-dev branch. Discussion ---------- Add configuration of root namespace This PR adds configuration for namespaces used in `make:*` commands that was discussed in #133. I've used [this doc](https://symfony.com/doc/current/bundles/configuration.html) in work and examples from `FrameworkBundle`. New parameter: `maker.root_namespace` - application root namespace, default: `App` To change it, you should create configuration file in `config/packages/dev` with name `maker.yaml` or `maker.xml` or `maker.php` with root node `maker`. For example YAML file: ```yaml # config/packages/dev/maker.yaml maker: root_namespace: 'App' ``` I've added some tests. I think we don't need Flex recipe but just document new feature. I need help for write documentation because my English. TODO: - [x] Tests - [x] Documentations - [ ] Squash commits Commits ------- 42b7008 Fixing accidental removal of property 545a422 Merge branch 'master' into configurable_namespace b92f19c Merge branch 'master' into configurable_namespace bf1b092 updating for now-missing property b5658c2 Merge branch 'master' into configurable_namespace 7440ade return type stuff 656116c Changing from an exception 5605d6e adding docs 23a715c Merge remote-tracking branch 'upstream/master' into configurable_namespace 152ffa6 The most important fix f34fa39 Add exception with hint message when root namespace is not found 758a2c9 Refactor AutoloaderUtil 5423250 Remove maker.root_namespace parameter and improve tests 7157e88 Fix file creation on Windows 657f653 Remove useless code a425953 Add functional tests for custom root namespace cases d4139d4 Make "abstract" command and remove entity namespace from config 51d7138 Add configuration of root and entity namespaces
2 parents 5f4d197 + 42b7008 commit f7ced83

File tree

30 files changed

+823
-127
lines changed

30 files changed

+823
-127
lines changed

src/Command/MakerCommand.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ protected function initialize(InputInterface $input, OutputInterface $output)
7272

7373
protected function interact(InputInterface $input, OutputInterface $output)
7474
{
75+
if (!$this->fileManager->isNamespaceConfiguredToAutoload($this->generator->getRootNamespace())) {
76+
$this->io->note([
77+
sprintf('It looks like your app may be using a namespace other than "%s".', $this->generator->getRootNamespace()),
78+
'To configure this and make your life easier, see: https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html#configuration.',
79+
]);
80+
}
81+
7582
foreach ($this->getDefinition()->getArguments() as $argument) {
7683
if ($input->getArgument($argument->getName())) {
7784
continue;

src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Bundle\MakerBundle\Command\MakerCommand;
1515
use Symfony\Bundle\MakerBundle\MakerInterface;
1616
use Symfony\Bundle\MakerBundle\Str;
17+
use Symfony\Component\DependencyInjection\ChildDefinition;
1718
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1819
use Symfony\Component\DependencyInjection\ContainerBuilder;
1920
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
@@ -32,14 +33,12 @@ public function process(ContainerBuilder $container)
3233
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, MakerInterface::class));
3334
}
3435

35-
$container->register(
36-
sprintf('maker.auto_command.%s', Str::asTwigVariable($class::getCommandName())),
37-
MakerCommand::class
38-
)->setArguments([
39-
new Reference($id),
40-
new Reference('maker.file_manager'),
41-
new Reference('maker.generator'),
42-
])->addTag('console.command', ['command' => $class::getCommandName()]);
36+
$commandDefinition = new ChildDefinition('maker.auto_command.abstract');
37+
$commandDefinition->setClass(MakerCommand::class);
38+
$commandDefinition->replaceArgument(0, new Reference($id));
39+
$commandDefinition->addTag('console.command', ['command' => $class::getCommandName()]);
40+
41+
$container->setDefinition(sprintf('maker.auto_command.%s', Str::asTwigVariable($class::getCommandName())), $commandDefinition);
4342
}
4443
}
4544
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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\DependencyInjection;
13+
14+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15+
use Symfony\Component\Config\Definition\ConfigurationInterface;
16+
17+
class Configuration implements ConfigurationInterface
18+
{
19+
/**
20+
* {@inheritdoc}
21+
*/
22+
public function getConfigTreeBuilder()
23+
{
24+
$treeBuilder = new TreeBuilder();
25+
$rootNode = $treeBuilder->root('maker');
26+
27+
$rootNode
28+
->children()
29+
->scalarNode('root_namespace')->defaultValue('App')->end()
30+
->end()
31+
;
32+
33+
return $treeBuilder;
34+
}
35+
}

src/DependencyInjection/MakerExtension.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ public function load(array $configs, ContainerBuilder $container)
3434
$loader->load('services.xml');
3535
$loader->load('makers.xml');
3636

37+
$configuration = $this->getConfiguration($configs, $container);
38+
$config = $this->processConfiguration($configuration, $configs);
39+
40+
$rootNamespace = trim($config['root_namespace'], '\\');
41+
42+
$makeCommandDefinition = $container->getDefinition('maker.generator');
43+
$makeCommandDefinition->replaceArgument(1, $rootNamespace);
44+
45+
$doctrineHelperDefinition = $container->getDefinition('maker.doctrine_helper');
46+
$doctrineHelperDefinition->replaceArgument(0, $rootNamespace.'\\Entity');
47+
3748
$container->registerForAutoconfiguration(MakerInterface::class)
3849
->addTag(MakeCommandRegistrationPass::MAKER_TAG);
3950
}

src/Doctrine/DoctrineHelper.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@
2828
*/
2929
final class DoctrineHelper
3030
{
31+
/**
32+
* @var string
33+
*/
34+
private $entityNamespace;
35+
3136
/**
3237
* @var ManagerRegistry
3338
*/
3439
private $registry;
3540

36-
public function __construct(ManagerRegistry $registry = null)
41+
public function __construct(string $entityNamespace, ManagerRegistry $registry = null)
3742
{
43+
$this->entityNamespace = trim($entityNamespace, '\\');
3844
$this->registry = $registry;
3945
}
4046

@@ -54,6 +60,11 @@ private function isDoctrineInstalled(): bool
5460
return null !== $this->registry;
5561
}
5662

63+
public function getEntityNamespace(): string
64+
{
65+
return $this->entityNamespace;
66+
}
67+
5768
/**
5869
* @param string $className
5970
*
@@ -94,7 +105,7 @@ public function getEntitiesForAutocomplete(): array
94105

95106
/* @var ClassMetadata $metadata */
96107
foreach (array_keys($allMetadata) as $classname) {
97-
$entityClassDetails = new ClassNameDetails($classname, 'App\\Entity');
108+
$entityClassDetails = new ClassNameDetails($classname, $this->entityNamespace);
98109
$entities[] = $entityClassDetails->getRelativeName();
99110
}
100111
}

src/FileManager.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ public function getNamespacePrefixForClass(string $className): string
163163
return $this->autoloaderUtil->getNamespacePrefixForClass($className);
164164
}
165165

166+
public function isNamespaceConfiguredToAutoload(string $namespace): bool
167+
{
168+
return $this->autoloaderUtil->isNamespaceConfiguredToAutoload($namespace);
169+
}
170+
166171
/**
167172
* Resolve '../' in paths (like real_path), but for non-existent files.
168173
*

src/Generator.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ class Generator
2525
private $pendingOperations = [];
2626
private $namespacePrefix;
2727

28-
public function __construct(FileManager $fileManager, $namespacePrefix)
28+
public function __construct(FileManager $fileManager, string $namespacePrefix)
2929
{
3030
$this->fileManager = $fileManager;
3131
$this->twigHelper = new GeneratorTwigHelper($fileManager);
32-
$this->namespacePrefix = rtrim($namespacePrefix, '\\');
32+
$this->namespacePrefix = trim($namespacePrefix, '\\');
3333
}
3434

3535
/**
@@ -170,4 +170,9 @@ public function writeChanges()
170170

171171
$this->pendingOperations = [];
172172
}
173+
174+
public function getRootNamespace(): string
175+
{
176+
return $this->namespacePrefix;
177+
}
173178
}

src/Maker/MakeEntity.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ final class MakeEntity extends AbstractMaker implements InputAwareMakerInterface
4848
private $doctrineHelper;
4949
private $generator;
5050

51-
public function __construct(FileManager $fileManager, string $projectDirectory, DoctrineHelper $doctrineHelper, Generator $generator = null)
51+
public function __construct(FileManager $fileManager, DoctrineHelper $doctrineHelper, string $projectDirectory, Generator $generator = null)
5252
{
5353
$this->fileManager = $fileManager;
5454
$this->doctrineHelper = $doctrineHelper;
@@ -92,7 +92,7 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
9292
'This command will generate any missing methods (e.g. getters & setters) for a class or all classes in a namespace.',
9393
'To overwrite any existing methods, re-run this command with the --overwrite flag',
9494
], null, 'fg=yellow');
95-
$classOrNamespace = $io->ask('Enter a class or namespace to regenerate', 'App\\Entity', [Validator::class, 'notBlank']);
95+
$classOrNamespace = $io->ask('Enter a class or namespace to regenerate', $this->getEntityNamespace(), [Validator::class, 'notBlank']);
9696

9797
$input->setArgument('name', $classOrNamespace);
9898

@@ -518,14 +518,14 @@ private function askRelationDetails(ConsoleStyle $io, string $generatedEntityCla
518518
$targetEntityClass = $io->askQuestion($question);
519519

520520
if (!class_exists($targetEntityClass)) {
521-
if (!class_exists('App\\Entity\\'.$targetEntityClass)) {
521+
if (!class_exists($this->getEntityNamespace().'\\'.$targetEntityClass)) {
522522
$io->error(sprintf('Unknown class "%s"', $targetEntityClass));
523523
$targetEntityClass = null;
524524

525525
continue;
526526
}
527527

528-
$targetEntityClass = 'App\\Entity\\'.$targetEntityClass;
528+
$targetEntityClass = $this->getEntityNamespace().'\\'.$targetEntityClass;
529529
}
530530
}
531531

@@ -841,4 +841,9 @@ private function doesEntityUseAnnotationMapping(string $className): bool
841841

842842
return $driver instanceof AnnotationDriver;
843843
}
844+
845+
private function getEntityNamespace(): string
846+
{
847+
return $this->doctrineHelper->getEntityNamespace();
848+
}
844849
}

src/Resources/config/makers.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727

2828
<service id="maker.maker.make_entity" class="Symfony\Bundle\MakerBundle\Maker\MakeEntity">
2929
<argument type="service" id="maker.file_manager" />
30-
<argument>%kernel.project_dir%</argument>
3130
<argument type="service" id="maker.doctrine_helper" />
31+
<argument>%kernel.project_dir%</argument>
3232
<argument type="service" id="maker.generator" />
3333
<tag name="maker.command" />
3434
</service>

src/Resources/config/services.xml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
<argument>%kernel.project_dir%</argument>
1414
</service>
1515

16-
<service id="maker.autoloader_util" class="Symfony\Bundle\MakerBundle\Util\AutoloaderUtil" />
16+
<service id="maker.autoloader_finder" class="Symfony\Bundle\MakerBundle\Util\ComposerAutoloaderFinder" />
17+
18+
<service id="maker.autoloader_util" class="Symfony\Bundle\MakerBundle\Util\AutoloaderUtil">
19+
<argument type="service" id="maker.autoloader_finder" />
20+
</service>
1721

1822
<service id="maker.event_registry" class="Symfony\Bundle\MakerBundle\EventRegistry">
1923
<argument type="service" id="event_dispatcher" />
@@ -24,12 +28,19 @@
2428
</service>
2529

2630
<service id="maker.doctrine_helper" class="Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper">
27-
<argument type="service" id="doctrine" on-invalid="ignore"/>
31+
<argument /> <!-- entity namespace -->
32+
<argument type="service" id="doctrine" on-invalid="ignore" />
33+
</service>
34+
35+
<service id="maker.auto_command.abstract" class="Symfony\Bundle\MakerBundle\Command\MakerCommand" abstract="true">
36+
<argument /> <!-- maker -->
37+
<argument type="service" id="maker.file_manager" />
38+
<argument type="service" id="maker.generator" />
2839
</service>
2940

3041
<service id="maker.generator" class="Symfony\Bundle\MakerBundle\Generator">
3142
<argument type="service" id="maker.file_manager" />
32-
<argument>App\</argument>
43+
<argument /> <!-- root namespace -->
3344
</service>
3445
</services>
3546
</container>

src/Resources/doc/index.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ optional arguments and options. Check them out with the ``--help`` option:
4141
4242
$ php bin/console make:controller --help
4343
44+
Configuration
45+
-------------
46+
47+
This bundles doesn't require any configuration. But, you *can* configure
48+
the root namespace that is used to "guess" what classes you want to generate:
49+
50+
.. code-block:: yaml
51+
52+
# config/packages/maker.yaml
53+
# create this file if you need to configure anything
54+
maker:
55+
# tell MakerBundle that all of your classes lives in an
56+
# Acme namespace, instead of the default App
57+
# (e.g. Acme\Entity\Article, Acme\Command\MyCommand, etc)
58+
root_namespace: 'Acme'
59+
4460
Creating your Own Makers
4561
------------------------
4662

src/Test/MakerTestDetails.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ final class MakerTestDetails
3838

3939
private $commandAllowedToFail = false;
4040

41+
private $snapshotSuffix = '';
42+
4143
private $requiredPhpVersion;
4244

4345
/**
@@ -64,6 +66,46 @@ public function setFixtureFilesPath(string $fixtureFilesPath): self
6466
return $this;
6567
}
6668

69+
public function changeRootNamespace(string $rootNamespace): self
70+
{
71+
$rootNamespace = trim($rootNamespace, '\\');
72+
73+
// to bypass read before flush issue
74+
$this->snapshotSuffix = $rootNamespace;
75+
76+
return $this
77+
->addReplacement(
78+
'composer.json',
79+
'"App\\\\": "src/"',
80+
'"'.$rootNamespace.'\\\\": "src/"'
81+
)
82+
->addReplacement(
83+
'src/Kernel.php',
84+
'namespace App',
85+
'namespace '.$rootNamespace
86+
)
87+
->addReplacement(
88+
'bin/console',
89+
'use App\\Kernel',
90+
'use '.$rootNamespace.'\\Kernel'
91+
)
92+
->addReplacement(
93+
'public/index.php',
94+
'use App\\Kernel',
95+
'use '.$rootNamespace.'\\Kernel'
96+
)
97+
->addReplacement(
98+
'config/services.yaml',
99+
'App\\',
100+
$rootNamespace.'\\'
101+
)
102+
->addReplacement(
103+
'phpunit.xml.dist',
104+
'<env name="KERNEL_CLASS" value="App\\Kernel" />',
105+
'<env name="KERNEL_CLASS" value="'.$rootNamespace.'\\Kernel" />'
106+
);
107+
}
108+
67109
public function addPreMakeCommand(string $preMakeCommand): self
68110
{
69111
$this->preMakeCommands[] = $preMakeCommand;
@@ -202,7 +244,7 @@ public function getUniqueCacheDirectoryName(): string
202244
{
203245
// for cache purposes, only the dependencies are important
204246
// shortened to avoid long paths on Windows
205-
return 'maker_'.substr(md5(serialize($this->getDependencies())), 0, 10);
247+
return 'maker_'.substr(md5(serialize($this->getDependencies()).$this->snapshotSuffix), 0, 10);
206248
}
207249

208250
public function getPreMakeCommands(): array

src/Test/MakerTestEnvironment.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,11 @@ private function preMake()
135135

136136
public function runMaker()
137137
{
138+
$this->preMake();
139+
138140
MakerTestProcess::create('php bin/console cache:clear --no-ansi', $this->path)
139141
->run();
140142

141-
$this->preMake();
142-
143143
// We don't need ansi coloring in tests!
144144
$testProcess = MakerTestProcess::create(
145145
sprintf('php bin/console %s %s --no-ansi', $this->testDetails->getMaker()::getCommandName(), $this->testDetails->getArgumentsString()),

0 commit comments

Comments
 (0)