diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index c9d94d086..15c718318 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -24,6 +24,8 @@ public function getConfigTreeBuilder(): TreeBuilder $rootNode ->children() ->scalarNode('root_namespace')->defaultValue('App')->end() + ->booleanNode('generate_final_classes')->defaultTrue()->end() + ->booleanNode('generate_final_entities')->defaultFalse()->end() ->end() ; diff --git a/src/DependencyInjection/MakerExtension.php b/src/DependencyInjection/MakerExtension.php index 51069bcf4..1de775bff 100644 --- a/src/DependencyInjection/MakerExtension.php +++ b/src/DependencyInjection/MakerExtension.php @@ -45,6 +45,13 @@ public function load(array $configs, ContainerBuilder $container): void $doctrineHelperDefinition = $container->getDefinition('maker.doctrine_helper'); $doctrineHelperDefinition->replaceArgument(0, $rootNamespace.'\\Entity'); + $componentGeneratorDefinition = $container->getDefinition('maker.template_component_generator'); + $componentGeneratorDefinition + ->replaceArgument(0, $config['generate_final_classes']) + ->replaceArgument(1, $config['generate_final_entities']) + ->replaceArgument(2, $rootNamespace) + ; + $container->registerForAutoconfiguration(MakerInterface::class) ->addTag(MakeCommandRegistrationPass::MAKER_TAG); } diff --git a/src/Generator.php b/src/Generator.php index 03f7984d6..315b33427 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -14,6 +14,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; use Symfony\Bundle\MakerBundle\Util\ClassNameDetails; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; use Symfony\Bundle\MakerBundle\Util\TemplateComponentGenerator; @@ -54,6 +55,11 @@ public function __construct( */ public function generateClass(string $className, string $templateName, array $variables = []): string { + if (\array_key_exists('class_data', $variables) && $variables['class_data'] instanceof ClassData) { + $classData = $this->templateComponentGenerator->configureClass($variables['class_data']); + $className = $classData->getFullClassName(); + } + $targetPath = $this->fileManager->getRelativePathForFutureClass($className); if (null === $targetPath) { diff --git a/src/Maker/MakeCrud.php b/src/Maker/MakeCrud.php index 46facba78..0a37b2ba4 100644 --- a/src/Maker/MakeCrud.php +++ b/src/Maker/MakeCrud.php @@ -27,7 +27,7 @@ use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait; use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer; use Symfony\Bundle\MakerBundle\Str; -use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; use Symfony\Bundle\MakerBundle\Validator; use Symfony\Bundle\TwigBundle\TwigBundle; use Symfony\Component\Console\Command\Command; @@ -147,6 +147,21 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen ++$iter; } while (class_exists($formClassDetails->getFullName())); + $controllerClassData = ClassData::create( + class: \sprintf('Controller\%s', $this->controllerClassName), + suffix: 'Controller', + extendsClass: AbstractController::class, + useStatements: [ + $entityClassDetails->getFullName(), + $formClassDetails->getFullName(), + $repositoryClassName, + AbstractController::class, + Request::class, + Response::class, + Route::class, + ], + ); + $entityVarPlural = lcfirst($this->inflector->pluralize($entityClassDetails->getShortName())); $entityVarSingular = lcfirst($this->inflector->singularize($entityClassDetails->getShortName())); @@ -156,25 +171,15 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $routeName = Str::asRouteName($controllerClassDetails->getRelativeNameWithoutSuffix()); $templatesPath = Str::asFilePath($controllerClassDetails->getRelativeNameWithoutSuffix()); - $useStatements = new UseStatementGenerator([ - $entityClassDetails->getFullName(), - $formClassDetails->getFullName(), - $repositoryClassName, - AbstractController::class, - Request::class, - Response::class, - Route::class, - ]); - if (EntityManagerInterface::class !== $repositoryClassName) { - $useStatements->addUseStatement(EntityManagerInterface::class); + $controllerClassData->addUseStatement(EntityManagerInterface::class); } $generator->generateController( - $controllerClassDetails->getFullName(), + $controllerClassData->getFullClassName(), 'crud/controller/Controller.tpl.php', array_merge([ - 'use_statements' => $useStatements, + 'class_data' => $controllerClassData, 'entity_class_name' => $entityClassDetails->getShortName(), 'form_class_name' => $formClassDetails->getShortName(), 'route_path' => Str::asRoutePath($controllerClassDetails->getRelativeNameWithoutSuffix()), @@ -242,37 +247,33 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen } if ($this->shouldGenerateTests()) { - $testClassDetails = $generator->createClassNameDetails( - $entityClassDetails->getRelativeNameWithoutSuffix(), - 'Test\\Controller\\', - 'ControllerTest' + $testClassData = ClassData::create( + class: \sprintf('Tests\Controller\%s', $entityClassDetails->getRelativeNameWithoutSuffix()), + suffix: 'ControllerTest', + extendsClass: WebTestCase::class, + useStatements: [ + $entityClassDetails->getFullName(), + WebTestCase::class, + KernelBrowser::class, + $repositoryClassName, + EntityRepository::class, + ], ); - $useStatements = new UseStatementGenerator([ - $entityClassDetails->getFullName(), - WebTestCase::class, - KernelBrowser::class, - $repositoryClassName, - ]); - - $useStatements->addUseStatement(EntityRepository::class); - if (EntityManagerInterface::class !== $repositoryClassName) { - $useStatements->addUseStatement(EntityManagerInterface::class); + $testClassData->addUseStatement(EntityManagerInterface::class); } - $generator->generateFile( - 'tests/Controller/'.$testClassDetails->getShortName().'.php', + $generator->generateClass( + $testClassData->getFullClassName(), 'crud/test/Test.EntityManager.tpl.php', [ - 'use_statements' => $useStatements, + 'class_data' => $testClassData, 'entity_full_class_name' => $entityClassDetails->getFullName(), 'entity_class_name' => $entityClassDetails->getShortName(), 'entity_var_singular' => $entityVarSingular, 'route_path' => Str::asRoutePath($controllerClassDetails->getRelativeNameWithoutSuffix()), 'route_name' => $routeName, - 'class_name' => Str::getShortClassName($testClassDetails->getFullName()), - 'namespace' => Str::getNamespace($testClassDetails->getFullName()), 'form_fields' => $entityDoctrineDetails->getFormFields(), 'repository_class_name' => EntityManagerInterface::class, 'form_field_prefix' => strtolower(Str::asSnakeCase($entityTwigVarSingular)), diff --git a/src/Maker/MakeVoter.php b/src/Maker/MakeVoter.php index b807bc36e..faabc993e 100644 --- a/src/Maker/MakeVoter.php +++ b/src/Maker/MakeVoter.php @@ -15,10 +15,13 @@ use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; 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 @@ -46,16 +49,21 @@ public function configureCommand(Command $command, InputConfiguration $inputConf public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void { - $voterClassNameDetails = $generator->createClassNameDetails( - $input->getArgument('name'), - 'Security\\Voter\\', - 'Voter' + $voterClassData = ClassData::create( + class: \sprintf('Security\Voter\%s', $input->getArgument('name')), + suffix: 'Voter', + extendsClass: Voter::class, + useStatements: [ + TokenInterface::class, + Voter::class, + UserInterface::class, + ] ); $generator->generateClass( - $voterClassNameDetails->getFullName(), + $voterClassData->getFullClassName(), 'security/Voter.tpl.php', - [] + ['class_data' => $voterClassData] ); $generator->writeChanges(); diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index ae08be6cb..5fbc0439b 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -80,6 +80,9 @@ + + + diff --git a/src/Resources/skeleton/crud/controller/Controller.tpl.php b/src/Resources/skeleton/crud/controller/Controller.tpl.php index 012ce710f..135d0c10a 100644 --- a/src/Resources/skeleton/crud/controller/Controller.tpl.php +++ b/src/Resources/skeleton/crud/controller/Controller.tpl.php @@ -1,11 +1,11 @@ -namespace ; +namespace getNamespace() ?>; - +getUseStatements(); ?> #[Route('')] -class extends AbstractController +getClassDeclaration() ?> { generateRouteForControllerMethod('/', sprintf('%s_index', $route_name), ['GET']) ?> diff --git a/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php b/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php index d5aaf90a8..825737383 100644 --- a/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php +++ b/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php @@ -3,9 +3,9 @@ namespace ; - +getUseStatements(); ?> -class extends WebTestCase +getClassDeclaration() ?> { private KernelBrowser $client; private EntityManagerInterface $manager; diff --git a/src/Resources/skeleton/security/Voter.tpl.php b/src/Resources/skeleton/security/Voter.tpl.php index 17d3ae5cb..431b3c585 100644 --- a/src/Resources/skeleton/security/Voter.tpl.php +++ b/src/Resources/skeleton/security/Voter.tpl.php @@ -1,12 +1,10 @@ -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(); ?> -class extends Voter +getClassDeclaration() ?> { public const EDIT = 'POST_EDIT'; public const VIEW = 'POST_VIEW'; @@ -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 new file mode 100644 index 000000000..988404f2a --- /dev/null +++ b/src/Util/ClassSource/Model/ClassData.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Util\ClassSource\Model; + +use Symfony\Bundle\MakerBundle\Str; +use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator; + +/** + * @author Jesse Rushlow + * + * @internal + */ +final class ClassData +{ + private function __construct( + 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 $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) { + $useStatements->addUseStatement($extendsClass); + } + + return new self( + className: Str::asClassName($className), + namespace: Str::getNamespace($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 = ''; + + if (null !== $this->extends) { + $extendsDeclaration = \sprintf(' extends %s', $this->extends); + } + + return \sprintf('%sclass %s%s', + $this->isFinal ? 'final ' : '', + $this->className, + $extendsDeclaration, + ); + } + + public function setIsFinal(bool $isFinal): self + { + $this->isFinal = $isFinal; + + return $this; + } + + public function addUseStatement(array|string $useStatement): self + { + $this->useStatementGenerator->addUseStatement($useStatement); + + return $this; + } + + public function getUseStatements(): string + { + return (string) $this->useStatementGenerator; + } +} diff --git a/src/Util/TemplateComponentGenerator.php b/src/Util/TemplateComponentGenerator.php index 03af60648..24271fc35 100644 --- a/src/Util/TemplateComponentGenerator.php +++ b/src/Util/TemplateComponentGenerator.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\MakerBundle\Util; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; + /** * @author Jesse Rushlow * @@ -18,6 +20,13 @@ */ final class TemplateComponentGenerator { + public function __construct( + private bool $generateFinalClasses, + private bool $generateFinalEntities, + private string $rootNamespace, + ) { + } + public function generateRouteForControllerMethod(string $routePath, string $routeName, array $methods = [], bool $indent = true, bool $trailingNewLine = true): string { $attribute = \sprintf('%s#[Route(\'%s\', name: \'%s\'', $indent ? ' ' : null, $routePath, $routeName); @@ -43,4 +52,15 @@ public function getPropertyType(ClassNameDetails $classNameDetails): ?string { return \sprintf('%s ', $classNameDetails->getShortName()); } + + public function configureClass(ClassData $classMetadata): ClassData + { + $classMetadata->setRootNamespace($this->rootNamespace); + + if ($classMetadata->isEntity) { + return $classMetadata->setIsFinal($this->generateFinalEntities); + } + + return $classMetadata->setIsFinal($this->generateFinalClasses); + } } diff --git a/tests/Doctrine/EntityRegeneratorTest.php b/tests/Doctrine/EntityRegeneratorTest.php index 8a095a413..4d2b251cb 100644 --- a/tests/Doctrine/EntityRegeneratorTest.php +++ b/tests/Doctrine/EntityRegeneratorTest.php @@ -23,6 +23,7 @@ use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\Util\AutoloaderUtil; use Symfony\Bundle\MakerBundle\Util\MakerFileLinkFormatter; +use Symfony\Bundle\MakerBundle\Util\TemplateComponentGenerator; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Filesystem\Filesystem; @@ -99,7 +100,8 @@ private function doTestRegeneration(string $sourceDir, Kernel $kernel, string $n $fileManager = new FileManager($fs, $autoloaderUtil, new MakerFileLinkFormatter(null), $tmpDir); $doctrineHelper = new DoctrineHelper('App\\Entity', $container->get('doctrine')); - $generator = new Generator($fileManager, 'App\\'); + $templateComponentGenerator = new TemplateComponentGenerator(false, false, 'App'); + $generator = new Generator(fileManager: $fileManager, namespacePrefix: 'App\\', templateComponentGenerator: $templateComponentGenerator); $entityClassGenerator = new EntityClassGenerator($generator, $doctrineHelper); $regenerator = new EntityRegenerator( $doctrineHelper, diff --git a/tests/Maker/MakeVoterTest.php b/tests/Maker/MakeVoterTest.php index c10af66d7..e6494aae2 100644 --- a/tests/Maker/MakeVoterTest.php +++ b/tests/Maker/MakeVoterTest.php @@ -14,6 +14,7 @@ use Symfony\Bundle\MakerBundle\Maker\MakeVoter; use Symfony\Bundle\MakerBundle\Test\MakerTestCase; use Symfony\Bundle\MakerBundle\Test\MakerTestRunner; +use Symfony\Component\Yaml\Yaml; class MakeVoterTest extends MakerTestCase { @@ -32,6 +33,32 @@ public function getTestDetails(): \Generator 'FooBar', ] ); + + $expectedVoterPath = \dirname(__DIR__).'/fixtures/make-voter/expected/FooBarVoter.php'; + $generatedVoter = $runner->getPath('src/Security/Voter/FooBarVoter.php'); + + self::assertSame(file_get_contents($expectedVoterPath), file_get_contents($generatedVoter)); + }), + ]; + + yield 'it_makes_voter_not_final' => [$this->createMakerTest() + ->run(function (MakerTestRunner $runner) { + $runner->writeFile( + 'config/packages/dev/maker.yaml', + Yaml::dump(['when@dev' => ['maker' => ['generate_final_classes' => false]]]) + ); + + $runner->runMaker( + [ + // voter class name + 'FooBar', + ] + ); + + $expectedVoterPath = \dirname(__DIR__).'/fixtures/make-voter/expected/not_final_FooBarVoter.php'; + $generatedVoter = $runner->getPath('src/Security/Voter/FooBarVoter.php'); + + self::assertSame(file_get_contents($expectedVoterPath), file_get_contents($generatedVoter)); }), ]; } diff --git a/tests/Util/ClassSource/ClassDataTest.php b/tests/Util/ClassSource/ClassDataTest.php new file mode 100644 index 000000000..cbad75dec --- /dev/null +++ b/tests/Util/ClassSource/ClassDataTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Tests\Util\ClassSource; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\MakerBundle\MakerBundle; +use Symfony\Bundle\MakerBundle\Test\MakerTestKernel; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; + +class ClassDataTest extends TestCase +{ + public function testStaticConstructor(): void + { + $meta = ClassData::create(MakerBundle::class); + + // Sanity check in case Maker's NS changes + self::assertSame('Symfony\Bundle\MakerBundle\MakerBundle', MakerBundle::class); + + 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 + { + $meta = ClassData::create(MakerBundle::class); + + self::assertSame('final class MakerBundle', $meta->getClassDeclaration()); + } + + public function testIsFinal(): void + { + $meta = ClassData::create(MakerBundle::class); + + // Default - isFinal - true + self::assertSame('final class MakerBundle', $meta->getClassDeclaration()); + + // Not Final - isFinal - false + $meta->setIsFinal(false); + self::assertSame('class MakerBundle', $meta->getClassDeclaration()); + } + + public function testGetClassDeclarationWithExtends(): void + { + $meta = ClassData::create(class: MakerBundle::class, extendsClass: MakerTestKernel::class); + + 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']; + } +} diff --git a/tests/Util/TemplateComponentGeneratorTest.php b/tests/Util/TemplateComponentGeneratorTest.php index 4c756e104..a61bb6bd2 100644 --- a/tests/Util/TemplateComponentGeneratorTest.php +++ b/tests/Util/TemplateComponentGeneratorTest.php @@ -12,7 +12,8 @@ namespace Symfony\Bundle\MakerBundle\Tests\Util; use PHPUnit\Framework\TestCase; -use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; +use Symfony\Bundle\MakerBundle\MakerBundle; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; use Symfony\Bundle\MakerBundle\Util\TemplateComponentGenerator; /** @@ -22,7 +23,7 @@ class TemplateComponentGeneratorTest extends TestCase { public function testRouteAttributes(): void { - $generator = new TemplateComponentGenerator($this->createMock(PhpCompatUtil::class)); + $generator = new TemplateComponentGenerator(false, false, 'App'); $expected = " #[Route('/', name: 'app_home')]\n"; @@ -34,7 +35,7 @@ public function testRouteAttributes(): void */ public function testRouteMethods(string $expected, array $methods): void { - $generator = new TemplateComponentGenerator($this->createMock(PhpCompatUtil::class)); + $generator = new TemplateComponentGenerator(false, false, 'App'); self::assertSame($expected, $generator->generateRouteForControllerMethod( '/', @@ -54,7 +55,7 @@ public function routeMethodDataProvider(): \Generator */ public function testRouteIndentation(string $expected): void { - $generator = new TemplateComponentGenerator($this->createMock(PhpCompatUtil::class)); + $generator = new TemplateComponentGenerator(false, false, 'App'); self::assertSame($expected, $generator->generateRouteForControllerMethod( '/', @@ -74,7 +75,7 @@ public function routeIndentationDataProvider(): \Generator */ public function testRouteTrailingNewLine(string $expected): void { - $generator = new TemplateComponentGenerator($this->createMock(PhpCompatUtil::class)); + $generator = new TemplateComponentGenerator(false, false, 'App'); self::assertSame($expected, $generator->generateRouteForControllerMethod( '/', @@ -89,4 +90,41 @@ public function routeTrailingNewLineDataProvider(): \Generator { yield ["#[Route('/', name: 'app_home')]", true]; } + + /** + * @dataProvider finalClassDataProvider + */ + public function testGetFinalClassDeclaration(bool $finalClass, bool $finalEntity, bool $isEntity, string $expectedResult): void + { + $generator = new TemplateComponentGenerator($finalClass, $finalEntity, 'App'); + + $classData = ClassData::create(MakerBundle::class, isEntity: $isEntity); + + $generator->configureClass($classData); + + self::assertSame(\sprintf('%sclass MakerBundle', $expectedResult), $classData->getClassDeclaration()); + } + + public function finalClassDataProvider(): \Generator + { + yield 'Not Final Class' => [false, false, false, '']; + yield 'Not Final Class w/ Entity' => [false, true, false, '']; + yield 'Final Class' => [true, false, false, 'final ']; + yield 'Final Class w/ Entity' => [true, true, false, 'final ']; + yield 'Not Final Entity' => [false, false, true, '']; + yield 'Not Final Entity w/ Class' => [true, false, true, '']; + yield 'Final Entity' => [false, true, true, 'final ']; + yield 'Final Entity w/ Class' => [true, true, true, 'final ']; + } + + public function testConfiguresClassDataWithRootNamespace(): void + { + $generator = new TemplateComponentGenerator(false, false, 'MakerTest'); + + $classData = ClassData::create(MakerBundle::class); + + $generator->configureClass($classData); + + self::assertSame('MakerTest\Symfony\Bundle\MakerBundle', $classData->getNamespace()); + } } diff --git a/tests/fixtures/make-crud/expected/WithCustomRepository.php b/tests/fixtures/make-crud/expected/WithCustomRepository.php index 390c94bf8..b1c77ffd5 100644 --- a/tests/fixtures/make-crud/expected/WithCustomRepository.php +++ b/tests/fixtures/make-crud/expected/WithCustomRepository.php @@ -12,7 +12,7 @@ use Symfony\Component\Routing\Attribute\Route; #[Route('/sweet/food')] -class SweetFoodController extends AbstractController +final class SweetFoodController extends AbstractController { #[Route('/', name: 'app_sweet_food_index', methods: ['GET'])] public function index(SweetFoodRepository $sweetFoodRepository): Response diff --git a/tests/fixtures/make-voter/expected/FooBarVoter.php b/tests/fixtures/make-voter/expected/FooBarVoter.php new file mode 100644 index 000000000..2cfc1ee22 --- /dev/null +++ b/tests/fixtures/make-voter/expected/FooBarVoter.php @@ -0,0 +1,46 @@ +getUser(); + + // if the user is anonymous, do not grant access + if (!$user instanceof UserInterface) { + return false; + } + + // ... (check conditions and return true to grant permission) ... + switch ($attribute) { + case self::EDIT: + // logic to determine if the user can EDIT + // return true or false + break; + + case self::VIEW: + // logic to determine if the user can VIEW + // return true or false + break; + } + + return false; + } +} diff --git a/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php b/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php new file mode 100644 index 000000000..e38fb4ad2 --- /dev/null +++ b/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php @@ -0,0 +1,46 @@ +getUser(); + + // if the user is anonymous, do not grant access + if (!$user instanceof UserInterface) { + return false; + } + + // ... (check conditions and return true to grant permission) ... + switch ($attribute) { + case self::EDIT: + // logic to determine if the user can EDIT + // return true or false + break; + + case self::VIEW: + // logic to determine if the user can VIEW + // return true or false + break; + } + + return false; + } +}