Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,12 @@ private function registerMakerConfiguration(ContainerBuilder $container, array $
return;
}

$namespaceprefix = $config['maker']['namespace_prefix'] ?? '';
if ('' !== $namespaceprefix) {
$namespaceprefix = trim($namespaceprefix, '\\').'\\';
}
$container->setParameter('api_platform.maker.namespace_prefix', $namespaceprefix);

$loader->load('maker.php');
}

Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,9 @@ private function addMakerSection(ArrayNodeDefinition $rootNode): void
->children()
->arrayNode('maker')
->{class_exists(MakerBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
->children()
->scalarNode('namespace_prefix')->defaultValue('')->info('Add a prefix to all maker generated classes. e.g set it to "Api" to set the maker namespace to "App\\Api\\" (if the maker.root_namespace config is App). e.g. App\\Api\\State\\MyStateProcessor')->end()
->end()
->end()
->end();
}
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Bundle/Resources/config/maker.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
$services = $container->services();

$services->set('api_platform.maker.command.state_processor', 'ApiPlatform\Symfony\Maker\MakeStateProcessor')
->args([service('api_platform.metadata.resource.name_collection_factory')])
Copy link
Contributor Author

@esserj esserj Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

service('api_platform.metadata.resource.name_collection_factory') was introduced in the original PR
But there was no dependency on this class then, or is now, so I'm assuming this was a leftover from an earlier approach

->args([param('api_platform.maker.namespace_prefix')])
->tag('maker.command');

$services->set('api_platform.maker.command.state_provider', 'ApiPlatform\Symfony\Maker\MakeStateProvider')
->args([service('api_platform.metadata.resource.name_collection_factory')])
->args([param('api_platform.maker.namespace_prefix')])
->tag('maker.command');

$services->set('api_platform.maker.command.filter', 'ApiPlatform\Symfony\Maker\MakeFilter')
->args([service('api_platform.metadata.resource.name_collection_factory')])
->args([param('api_platform.maker.namespace_prefix')])
->tag('maker.command');
};
8 changes: 7 additions & 1 deletion src/Symfony/Maker/MakeFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

final class MakeFilter extends AbstractMaker
{
public function __construct(private readonly string $namespacePrefix = '')
{
}

/**
* {@inheritdoc}
*/
Expand All @@ -50,6 +55,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
$command
->addArgument('type', InputArgument::REQUIRED, \sprintf('Choose a type for your filter (<fg=yellow>%s</>)', self::getFilterTypesAsString()))
->addArgument('name', InputArgument::REQUIRED, 'Choose a class name for your filter (e.g. <fg=yellow>AwesomeFilter</>)')
->addOption('namespace-prefix', 'p', InputOption::VALUE_REQUIRED, 'Specify the namespace prefix to use for the filter class', $this->namespacePrefix.'Filter')
->setHelp(file_get_contents(__DIR__.'/Resources/help/MakeFilter.txt'));
}

Expand All @@ -75,7 +81,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen

$filterNameDetails = $generator->createClassNameDetails(
name: $input->getArgument('name'),
namespacePrefix: 'Filter\\'
namespacePrefix: trim($input->getOption('namespace-prefix'), '\\').'\\'
);
$filterName = \sprintf('%sFilter', ucfirst($type->value));

Expand Down
8 changes: 7 additions & 1 deletion src/Symfony/Maker/MakeStateProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

final class MakeStateProcessor extends AbstractMaker
{
public function __construct(private readonly string $namespacePrefix = '')
{
}

/**
* {@inheritdoc}
*/
Expand All @@ -47,6 +52,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
{
$command
->addArgument('name', InputArgument::REQUIRED, 'Choose a class name for your state processor (e.g. <fg=yellow>AwesomeStateProcessor</>)')
->addOption('namespace-prefix', 'p', InputOption::VALUE_REQUIRED, 'Specify the namespace prefix to use for the state processor class', $this->namespacePrefix.'State')
->setHelp(file_get_contents(__DIR__.'/Resources/help/MakeStateProcessor.txt'));
}

Expand All @@ -64,7 +70,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
{
$stateProcessorClassNameDetails = $generator->createClassNameDetails(
$input->getArgument('name'),
'State\\'
trim($input->getOption('namespace-prefix'), '\\').'\\'
);

$generator->generateClass(
Expand Down
8 changes: 7 additions & 1 deletion src/Symfony/Maker/MakeStateProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

final class MakeStateProvider extends AbstractMaker
{
public function __construct(private readonly string $namespacePrefix = '')
{
}

/**
* {@inheritdoc}
*/
Expand All @@ -47,6 +52,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
{
$command
->addArgument('name', InputArgument::REQUIRED, 'Choose a class name for your state provider (e.g. <fg=yellow>AwesomeStateProvider</>)')
->addOption('namespace-prefix', 'p', InputOption::VALUE_REQUIRED, 'Specify the namespace prefix to use for the state provider class', $this->namespacePrefix.'State')
->setHelp(file_get_contents(__DIR__.'/Resources/help/MakeStateProvider.txt'));
}

Expand All @@ -64,7 +70,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
{
$stateProviderClassNameDetails = $generator->createClassNameDetails(
$input->getArgument('name'),
'State\\'
trim($input->getOption('namespace-prefix'), '\\').'\\'
);

$generator->generateClass(
Expand Down
29 changes: 29 additions & 0 deletions tests/Fixtures/Symfony/Maker/NamespacedCustomOrmFilter.fixture
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace App\Api\Filter;

use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\BackwardCompatibleFilterDescriptionTrait;
use ApiPlatform\Metadata\Operation;
use Doctrine\ORM\QueryBuilder;

class CustomOrmFilter implements FilterInterface
{
use BackwardCompatibleFilterDescriptionTrait; // Here for backward compatibility, keep it until 5.0.

public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
{
// Retrieve the parameter and it's value
// $parameter = $context['parameter'];
// $value = $parameter->getValue();

// Retrieve the property
// $property = $parameter->getProperty();

// Retrieve alias and parameter name
// $alias = $queryBuilder->getRootAliases()[0];
// $parameterName = $queryNameGenerator->generateParameterName($property);

// TODO: make your awesome query using the $queryBuilder
// $queryBuilder->
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace App\Api\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;

class CustomStateProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
{
// Handle the state
}
}
12 changes: 12 additions & 0 deletions tests/Fixtures/Symfony/Maker/NamespacedCustomStateProvider.fixture
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace App\Api\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;

class CustomStateProvider implements ProviderInterface
{
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
// Retrieve the state from somewhere
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
],
'maker' => [
'enabled' => true,
'namespace_prefix' => '',
],
'use_symfony_listeners' => false,
'handle_symfony_errors' => false,
Expand Down
19 changes: 19 additions & 0 deletions tests/Symfony/Maker/MakeFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ public function testMakeFilter(string $type, string $name, bool $isInteractive):
$this->assertStringContainsString(' Next: Open your filter class and start customizing it.', $display);
}

public function testMakeFilterWithCustomNamespace(): void
{
$inputs = ['name' => 'CustomOrmFilter', 'type' => 'orm', '--namespace-prefix' => 'Api\\Filter\\'];
$newFilterFile = self::tempFile('src/Api/Filter/CustomOrmFilter.php');

$tester = new CommandTester((new Application(self::bootKernel()))->find('make:filter'));
$tester->execute($inputs);

$this->assertFileExists($newFilterFile);

// Unify line endings
$expected = preg_replace('~\R~u', "\r\n", file_get_contents(__DIR__.'/../../Fixtures/Symfony/Maker/NamespacedCustomOrmFilter.fixture'));
$result = preg_replace('~\R~u', "\r\n", file_get_contents($newFilterFile));
$this->assertStringContainsString($expected, $result);

$display = $tester->getDisplay();
$this->assertStringContainsString('Success!', $display);
}

public static function filterProvider(): \Generator
{
yield 'Generate ORM filter' => ['orm', 'CustomOrmFilter', true];
Expand Down
19 changes: 19 additions & 0 deletions tests/Symfony/Maker/MakeStateProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ public function testMakeStateProcessor(bool $isInteractive): void
$this->assertStringContainsString('Next: Open your new state processor class and start customizing it.', $display);
}

public function testMakeStateProcessorWithCustomNamespace(): void
{
$inputs = ['name' => 'CustomStateProcessor', '--namespace-prefix' => 'Api\\State\\'];
$newProviderFile = self::tempFile('src/Api/State/CustomStateProcessor.php');

$tester = new CommandTester((new Application(self::bootKernel()))->find('make:state-processor'));
$tester->execute($inputs);

$this->assertFileExists($newProviderFile);

// Unify line endings
$expected = preg_replace('~\R~u', "\r\n", file_get_contents(__DIR__.'/../../Fixtures/Symfony/Maker/NamespacedCustomStateProcessor.fixture'));
$result = preg_replace('~\R~u', "\r\n", file_get_contents($newProviderFile));
$this->assertStringContainsString($expected, $result);

$display = $tester->getDisplay();
$this->assertStringContainsString('Success!', $display);
}

public static function stateProcessorProvider(): \Generator
{
yield 'Generate state processor' => [
Expand Down
19 changes: 19 additions & 0 deletions tests/Symfony/Maker/MakeStateProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ public function testMakeStateProvider(bool $isInteractive): void
$this->assertStringContainsString('Next: Open your new state provider class and start customizing it.', $display);
}

public function testMakeStateProviderWithCustomNamespace(): void
{
$inputs = ['name' => 'CustomStateProvider', '--namespace-prefix' => 'Api\\State\\'];
$newProviderFile = self::tempFile('src/Api/State/CustomStateProvider.php');

$tester = new CommandTester((new Application(self::bootKernel()))->find('make:state-provider'));
$tester->execute($inputs);

$this->assertFileExists($newProviderFile);

// Unify line endings
$expected = preg_replace('~\R~u', "\r\n", file_get_contents(__DIR__.'/../../Fixtures/Symfony/Maker/NamespacedCustomStateProvider.fixture'));
$result = preg_replace('~\R~u', "\r\n", file_get_contents($newProviderFile));
$this->assertStringContainsString($expected, $result);

$display = $tester->getDisplay();
$this->assertStringContainsString('Success!', $display);
}

public static function stateProviderDataProvider(): \Generator
{
yield 'Generate state provider' => [
Expand Down
Loading