From 522869b95842b6a9019eb782236e1a195b4c2716 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Sun, 19 May 2024 00:11:01 -0400 Subject: [PATCH] support for root namespaces and suffix's --- src/DependencyInjection/MakerExtension.php | 1 + src/Maker/MakeCrud.php | 10 +++-- src/Maker/MakeVoter.php | 22 +++++----- src/Resources/config/services.xml | 5 ++- .../crud/controller/Controller.tpl.php | 2 +- src/Resources/skeleton/security/Voter.tpl.php | 8 ++-- src/Util/ClassSource/Model/ClassData.php | 43 ++++++++++++++++--- src/Util/TemplateComponentGenerator.php | 3 ++ tests/Util/ClassSource/ClassDataTest.php | 42 ++++++++++++++++-- 9 files changed, 105 insertions(+), 31 deletions(-) diff --git a/src/DependencyInjection/MakerExtension.php b/src/DependencyInjection/MakerExtension.php index 7eb250ee24..1de775bff9 100644 --- a/src/DependencyInjection/MakerExtension.php +++ b/src/DependencyInjection/MakerExtension.php @@ -49,6 +49,7 @@ public function load(array $configs, ContainerBuilder $container): void $componentGeneratorDefinition ->replaceArgument(0, $config['generate_final_classes']) ->replaceArgument(1, $config['generate_final_entities']) + ->replaceArgument(2, $rootNamespace) ; $container->registerForAutoconfiguration(MakerInterface::class) diff --git a/src/Maker/MakeCrud.php b/src/Maker/MakeCrud.php index 6cc9ea2ce4..af8d0163e4 100644 --- a/src/Maker/MakeCrud.php +++ b/src/Maker/MakeCrud.php @@ -148,7 +148,8 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen } while (class_exists($formClassDetails->getFullName())); $controllerClassData = ClassData::create( - class: sprintf('App\Controller\%sController', $this->controllerClassName), + class: sprintf('Controller\%s', $this->controllerClassName), + suffix: 'Controller', extendsClass: AbstractController::class, useStatements: [ $entityClassDetails->getFullName(), @@ -175,7 +176,7 @@ class: sprintf('App\Controller\%sController', $this->controllerClassName), } $generator->generateController( - $controllerClassDetails->getFullName(), + $controllerClassData->getFullClassName(), 'crud/controller/Controller.tpl.php', array_merge([ 'class_data' => $controllerClassData, @@ -247,7 +248,8 @@ class: sprintf('App\Controller\%sController', $this->controllerClassName), if ($this->shouldGenerateTests()) { $testClassData = ClassData::create( - class: sprintf('App\Tests\Controller\%sControllerTest', $entityClassDetails->getRelativeNameWithoutSuffix()), + class: sprintf('Tests\Controller\%s', $entityClassDetails->getRelativeNameWithoutSuffix()), + suffix: 'ControllerTest', extendsClass: WebTestCase::class, useStatements: [ $entityClassDetails->getFullName(), @@ -263,7 +265,7 @@ class: sprintf('App\Tests\Controller\%sControllerTest', $entityClassDetails->get } $generator->generateClass( - $testClassData->fullClassName, + $testClassData->getFullClassName(), 'crud/test/Test.EntityManager.tpl.php', [ 'class_data' => $testClassData, diff --git a/src/Maker/MakeVoter.php b/src/Maker/MakeVoter.php index 5063b1e0b5..c489ca2eda 100644 --- a/src/Maker/MakeVoter.php +++ b/src/Maker/MakeVoter.php @@ -19,7 +19,9 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; +use Symfony\Component\Security\Core\User\UserInterface; /** * @author Javier Eguiluz @@ -47,21 +49,21 @@ public function configureCommand(Command $command, InputConfiguration $inputConf public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void { - $classMetaData = ClassData::create( - class: sprintf('App\Security\Voter\%sVoter', $input->getArgument('name')), + $voterClassData = ClassData::create( + class: sprintf('Security\Voter\%s', $input->getArgument('name')), + suffix: 'Voter', extendsClass: Voter::class, - ); - - $voterClassNameDetails = $generator->createClassNameDetails( - $input->getArgument('name'), - 'Security\\Voter\\', - 'Voter' + useStatements: [ + TokenInterface::class, + Voter::class, + UserInterface::class, + ] ); $generator->generateClass( - $voterClassNameDetails->getFullName(), + $voterClassData->getFullClassName(), 'security/Voter.tpl.php', - ['class_data' => $classMetaData] + ['class_data' => $voterClassData] ); $generator->writeChanges(); diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 55d26d7a48..5fbc0439ba 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -80,8 +80,9 @@ - - + + + diff --git a/src/Resources/skeleton/crud/controller/Controller.tpl.php b/src/Resources/skeleton/crud/controller/Controller.tpl.php index e50c62e0d5..135d0c10ad 100644 --- a/src/Resources/skeleton/crud/controller/Controller.tpl.php +++ b/src/Resources/skeleton/crud/controller/Controller.tpl.php @@ -1,6 +1,6 @@ -namespace ; +namespace getNamespace() ?>; getUseStatements(); ?> diff --git a/src/Resources/skeleton/security/Voter.tpl.php b/src/Resources/skeleton/security/Voter.tpl.php index 0b5ff0eb1d..431b3c5852 100644 --- a/src/Resources/skeleton/security/Voter.tpl.php +++ b/src/Resources/skeleton/security/Voter.tpl.php @@ -1,10 +1,8 @@ -namespace ; +namespace getNamespace(); ?>; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\Voter\Voter; -use Symfony\Component\Security\Core\User\UserInterface; +getUseStatements(); ?> getClassDeclaration() ?> { @@ -16,7 +14,7 @@ protected function supports(string $attribute, mixed $subject): bool // replace with your own logic // https://symfony.com/doc/current/security/voters.html return in_array($attribute, [self::EDIT, self::VIEW]) - && $subject instanceof \App\Entity\; + && $subject instanceof \App\Entity\getClassName()) ?>; } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Util/ClassSource/Model/ClassData.php b/src/Util/ClassSource/Model/ClassData.php index f967a69af3..9ef9faabae 100644 --- a/src/Util/ClassSource/Model/ClassData.php +++ b/src/Util/ClassSource/Model/ClassData.php @@ -22,18 +22,24 @@ final class ClassData { private function __construct( - public readonly string $className, - public readonly string $namespace, - public readonly string $fullClassName, + private string $className, + private string $namespace, public readonly ?string $extends, public readonly bool $isEntity, private UseStatementGenerator $useStatementGenerator, private bool $isFinal = true, + private string $rootNamespace = 'App', ) { } - public static function create(string $class, ?string $extendsClass = null, bool $isEntity = false, array $useStatements = []): self + public static function create(string $class, ?string $suffix = null, ?string $extendsClass = null, bool $isEntity = false, array $useStatements = []): self { + $className = Str::getShortClassName($class); + + if (null !== $suffix && !str_ends_with($className, $suffix)) { + $className = Str::asClassName(sprintf('%s%s', $className, $suffix)); + } + $useStatements = new UseStatementGenerator($useStatements); if ($extendsClass) { @@ -41,15 +47,40 @@ public static function create(string $class, ?string $extendsClass = null, bool } return new self( - className: Str::getShortClassName($class), + className: Str::asClassName($className), namespace: Str::getNamespace($class), - fullClassName: $class, extends: null === $extendsClass ? null : Str::getShortClassName($extendsClass), isEntity: $isEntity, useStatementGenerator: $useStatements, ); } + public function getClassName(): string + { + return $this->className; + } + + public function getNamespace(): string + { + if (empty($this->namespace)) { + return $this->rootNamespace; + } + + return sprintf('%s\%s', $this->rootNamespace, $this->namespace); + } + + public function getFullClassName(): string + { + return sprintf('%s\%s', $this->getNamespace(), $this->className); + } + + public function setRootNamespace(string $rootNamespace): self + { + $this->rootNamespace = $rootNamespace; + + return $this; + } + public function getClassDeclaration(): string { $extendsDeclaration = ''; diff --git a/src/Util/TemplateComponentGenerator.php b/src/Util/TemplateComponentGenerator.php index d985ed45af..2f9c977fd9 100644 --- a/src/Util/TemplateComponentGenerator.php +++ b/src/Util/TemplateComponentGenerator.php @@ -23,6 +23,7 @@ final class TemplateComponentGenerator public function __construct( private bool $generateFinalClasses, private bool $generateFinalEntities, + private string $rootNamespace, ) { } @@ -54,6 +55,8 @@ public function getPropertyType(ClassNameDetails $classNameDetails): ?string public function configureClass(ClassData $classMetadata): ClassData { + $classMetadata->setRootNamespace($this->rootNamespace); + if ($classMetadata->isEntity) { return $classMetadata->setIsFinal($this->generateFinalEntities); } diff --git a/tests/Util/ClassSource/ClassDataTest.php b/tests/Util/ClassSource/ClassDataTest.php index c82817e3e1..cbad75dece 100644 --- a/tests/Util/ClassSource/ClassDataTest.php +++ b/tests/Util/ClassSource/ClassDataTest.php @@ -25,9 +25,9 @@ public function testStaticConstructor(): void // Sanity check in case Maker's NS changes self::assertSame('Symfony\Bundle\MakerBundle\MakerBundle', MakerBundle::class); - self::assertSame('MakerBundle', $meta->className); - self::assertSame('Symfony\Bundle\MakerBundle', $meta->namespace); - self::assertSame('Symfony\Bundle\MakerBundle\MakerBundle', $meta->fullClassName); + self::assertSame('MakerBundle', $meta->getClassName()); + self::assertSame('App\Symfony\Bundle\MakerBundle', $meta->getNamespace()); + self::assertSame('App\Symfony\Bundle\MakerBundle\MakerBundle', $meta->getFullClassName()); } public function testGetClassDeclaration(): void @@ -55,4 +55,40 @@ public function testGetClassDeclarationWithExtends(): void self::assertSame('final class MakerBundle extends MakerTestKernel', $meta->getClassDeclaration()); } + + /** @dataProvider suffixDataProvider */ + public function testSuffix(?string $suffix, string $expectedResult): void + { + $data = ClassData::create(class: MakerBundle::class, suffix: $suffix); + + self::assertSame($expectedResult, $data->getClassName()); + } + + public function suffixDataProvider(): \Generator + { + yield [null, 'MakerBundle']; + yield ['Testing', 'MakerBundleTesting']; + yield ['Bundle', 'MakerBundle']; + } + + /** @dataProvider namespaceDataProvider */ + public function testNamespace(string $class, ?string $rootNamespace, string $expectedNamespace, string $expectedFullClassName): void + { + $class = ClassData::create($class); + + if (null !== $rootNamespace) { + $class->setRootNamespace($rootNamespace); + } + + self::assertSame($expectedNamespace, $class->getNamespace()); + self::assertSame($expectedFullClassName, $class->getFullClassName()); + } + + public function namespaceDataProvider(): \Generator + { + yield ['MyController', null, 'App', 'App\MyController']; + yield ['Controller\MyController', null, 'App\Controller', 'App\Controller\MyController']; + yield ['MyController', 'Maker', 'Maker', 'Maker\MyController']; + yield ['Controller\MyController', 'Maker', 'Maker\Controller', 'Maker\Controller\MyController']; + } }