Skip to content

Commit 9ad94f9

Browse files
committed
Add configurable naming strategy
1 parent 3f6ca0e commit 9ad94f9

File tree

13 files changed

+196
-9
lines changed

13 files changed

+196
-9
lines changed

src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public function load(array $configs, ContainerBuilder $container)
5757
}
5858
}
5959

60+
$container->setAlias('api_platform.routing.resource_path_generator', $config['routing']['resource_path_generator']);
61+
6062
if ($config['name_converter']) {
6163
$container->setAlias('api_platform.name_converter', $config['name_converter']);
6264
}

src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public function getConfigTreeBuilder()
5858
->end()
5959
->end()
6060
->end()
61+
->arrayNode('routing')
62+
->addDefaultsIfNotSet()
63+
->children()
64+
->scalarNode('resource_path_generator')->defaultValue('api_platform.routing.resource_path_generator.underscore')->info('Specify the strategy to use for generating resource paths.')->end()
65+
->end()
66+
->end()
6167
->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
6268
->booleanNode('enable_fos_user')->defaultValue(false)->info('Enable the FOSUserBundle integration.')->end()
6369
->booleanNode('enable_nelmio_api_doc')->defaultTrue()->info('Enable the Nelmio Api doc integration.')->end()

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<argument type="service" id="kernel" />
2727
<argument type="service" id="api_platform.metadata.resource.name_collection_factory" />
2828
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
29+
<argument type="service" id="api_platform.routing.resource_path_generator" />
2930

3031
<tag name="routing.loader" />
3132
</service>
@@ -44,6 +45,12 @@
4445

4546
<service id="api_platform.negotiator" class="Negotiation\Negotiator" public="false" />
4647

48+
<!-- Resource path generators -->
49+
50+
<service id="api_platform.routing.resource_path_generator.underscore" class="ApiPlatform\Core\Routing\UnderscoreResourcePathGenerator" public="false" />
51+
52+
<service id="api_platform.routing.resource_path_generator.dash" class="ApiPlatform\Core\Routing\DashResourcePathGenerator" public="false" />
53+
4754
<!-- Event listeners -->
4855

4956
<service id="api_platform.listener.request.format" class="ApiPlatform\Core\EventListener\FormatRequestListener">

src/Bridge/Symfony/Routing/ApiLoader.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
namespace ApiPlatform\Core\Bridge\Symfony\Routing;
1313

14+
use ApiPlatform\Core\Exception\InvalidResourceException;
1415
use ApiPlatform\Core\Exception\RuntimeException;
1516
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
1617
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
18+
use ApiPlatform\Core\Routing\ResourcePathGeneratorInterface;
1719
use Doctrine\Common\Inflector\Inflector;
1820
use Symfony\Component\Config\FileLocator;
1921
use Symfony\Component\Config\Loader\Loader;
@@ -35,12 +37,14 @@ final class ApiLoader extends Loader
3537
private $fileLoader;
3638
private $resourceNameCollectionFactory;
3739
private $resourceMetadataFactory;
40+
private $resourcePathGenerator;
3841

39-
public function __construct(KernelInterface $kernel, ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory)
42+
public function __construct(KernelInterface $kernel, ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourcePathGeneratorInterface $resourcePathGenerator)
4043
{
4144
$this->fileLoader = new XmlFileLoader(new FileLocator($kernel->locateResource('@ApiPlatformBundle/Resources/config/routing')));
4245
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
4346
$this->resourceMetadataFactory = $resourceMetadataFactory;
47+
$this->resourcePathGenerator = $resourcePathGenerator;
4448
}
4549

4650
/**
@@ -55,14 +59,18 @@ public function load($data, $type = null)
5559

5660
foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
5761
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
58-
$normalizedShortName = Inflector::pluralize(Inflector::tableize($resourceMetadata->getShortName()));
62+
$resourceShortName = $resourceMetadata->getShortName();
63+
64+
if (null === $resourceShortName) {
65+
throw new InvalidResourceException(sprintf('Resource %s has no short name defined.', $resourceClass));
66+
}
5967

6068
foreach ($resourceMetadata->getCollectionOperations() as $operationName => $operation) {
61-
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $normalizedShortName, true);
69+
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $resourceShortName, true);
6270
}
6371

6472
foreach ($resourceMetadata->getItemOperations() as $operationName => $operation) {
65-
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $normalizedShortName, false);
73+
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $resourceShortName, false);
6674
}
6775
}
6876

@@ -84,12 +92,12 @@ public function supports($resource, $type = null)
8492
* @param string $resourceClass
8593
* @param string $operationName
8694
* @param array $operation
87-
* @param string $normalizedShortName
95+
* @param string $resourceShortName
8896
* @param bool $collection
8997
*
9098
* @throws RuntimeException
9199
*/
92-
private function addRoute(RouteCollection $routeCollection, string $resourceClass, string $operationName, array $operation, string $normalizedShortName, bool $collection)
100+
private function addRoute(RouteCollection $routeCollection, string $resourceClass, string $operationName, array $operation, string $resourceShortName, bool $collection)
93101
{
94102
if (isset($operation['route_name'])) {
95103
return;
@@ -113,14 +121,15 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas
113121
$path = $operation['path'] ?? null;
114122

115123
if (null === $path) {
116-
$path = '/'.$normalizedShortName;
124+
$path = '/'.$this->resourcePathGenerator->generateResourceBasePath($resourceShortName);
117125

118126
if (!$collection) {
119127
$path .= '/{id}';
120128
}
121129
}
122130

123-
$routeName = sprintf('%s%s_%s', self::ROUTE_NAME_PREFIX, $normalizedShortName, $actionName);
131+
$resourceRouteName = Inflector::pluralize(Inflector::tableize($resourceShortName));
132+
$routeName = sprintf('%s%s_%s', self::ROUTE_NAME_PREFIX, $resourceRouteName, $actionName);
124133

125134
$route = new Route(
126135
$path,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[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 ApiPlatform\Core\Exception;
13+
14+
/**
15+
* Invalid resource exception.
16+
*
17+
* @author Paul Le Corre <[email protected]>
18+
*/
19+
class InvalidResourceException extends \Exception implements ExceptionInterface
20+
{
21+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[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 ApiPlatform\Core\Routing;
13+
14+
use Doctrine\Common\Inflector\Inflector;
15+
16+
/**
17+
* @author Paul Le Corre <[email protected]>
18+
*/
19+
class DashResourcePathGenerator implements ResourcePathGeneratorInterface
20+
{
21+
public function generateResourceBasePath(string $resourceShortName) : string
22+
{
23+
$pathName = strtolower(preg_replace('~(?<=\\w)([A-Z])~', '-$1', $resourceShortName));
24+
25+
return Inflector::pluralize($pathName);
26+
}
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[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 ApiPlatform\Core\Routing;
13+
14+
/**
15+
* @author Paul Le Corre <[email protected]>
16+
*/
17+
interface ResourcePathGeneratorInterface
18+
{
19+
public function generateResourceBasePath(string $resourceShortName) : string;
20+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[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 ApiPlatform\Core\Routing;
13+
14+
use Doctrine\Common\Inflector\Inflector;
15+
16+
/**
17+
* @author Paul Le Corre <[email protected]>
18+
*/
19+
class UnderscoreResourcePathGenerator implements ResourcePathGeneratorInterface
20+
{
21+
public function generateResourceBasePath(string $resourceShortName) : string
22+
{
23+
$pathName = Inflector::tableize($resourceShortName);
24+
25+
return Inflector::pluralize($pathName);
26+
}
27+
}

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ private function getContainerBuilderProphecy()
274274
'api_platform.route_loader',
275275
'api_platform.router',
276276
'api_platform.iri_converter',
277+
'api_platform.routing.resource_path_generator.underscore',
278+
'api_platform.routing.resource_path_generator.dash',
277279
'api_platform.listener.request.format',
278280
'api_platform.listener.view.validation',
279281
'api_platform.listener.request.format',
@@ -324,6 +326,7 @@ private function getContainerBuilderProphecy()
324326
}
325327

326328
$aliases = [
329+
'api_platform.routing.resource_path_generator' => 'api_platform.routing.resource_path_generator.underscore',
327330
'api_platform.metadata.resource.name_collection_factory' => 'api_platform.metadata.resource.name_collection_factory.annotation',
328331
'api_platform.metadata.resource.cache' => 'api_platform.metadata.resource.cache.array',
329332
'api_platform.metadata.property.cache' => 'api_platform.metadata.property.cache.array',

tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public function testDefaultConfig()
3434
'title' => 'title',
3535
'description' => 'description',
3636
'supported_formats' => ['jsonld' => ['mime_types' => ['application/ld+json']]],
37+
'routing' => [
38+
'resource_path_generator' => 'api_platform.routing.resource_path_generator.underscore',
39+
],
3740
'name_converter' => null,
3841
'enable_fos_user' => false,
3942
'enable_nelmio_api_doc' => true,

0 commit comments

Comments
 (0)