Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurable naming strategy #547

Merged
merged 1 commit into from
May 31, 2016
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 @@ -57,6 +57,8 @@ public function load(array $configs, ContainerBuilder $container)
}
}

$container->setAlias('api_platform.routing.resource_path_generator', $config['routing']['resource_path_generator']);

if ($config['name_converter']) {
$container->setAlias('api_platform.name_converter', $config['name_converter']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public function getConfigTreeBuilder()
->end()
->end()
->end()
->arrayNode('routing')
->addDefaultsIfNotSet()
->children()
->scalarNode('resource_path_generator')->defaultValue('api_platform.routing.resource_path_generator.underscore')->info('Specify the strategy to use for generating resource paths.')->end()
->end()
->end()
->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
->booleanNode('enable_fos_user')->defaultValue(false)->info('Enable the FOSUserBundle integration.')->end()
->booleanNode('enable_nelmio_api_doc')->defaultTrue()->info('Enable the Nelmio Api doc integration.')->end()
Expand Down
7 changes: 7 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<argument type="service" id="kernel" />
<argument type="service" id="api_platform.metadata.resource.name_collection_factory" />
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
<argument type="service" id="api_platform.routing.resource_path_generator" />

<tag name="routing.loader" />
</service>
Expand All @@ -44,6 +45,12 @@

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

<!-- Resource path generators -->

<service id="api_platform.routing.resource_path_generator.underscore" class="ApiPlatform\Core\Routing\UnderscoreResourcePathGenerator" public="false" />

<service id="api_platform.routing.resource_path_generator.dash" class="ApiPlatform\Core\Routing\DashResourcePathGenerator" public="false" />

<!-- Event listeners -->

<service id="api_platform.listener.request.format" class="ApiPlatform\Core\EventListener\FormatRequestListener">
Expand Down
25 changes: 17 additions & 8 deletions src/Bridge/Symfony/Routing/ApiLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

namespace ApiPlatform\Core\Bridge\Symfony\Routing;

use ApiPlatform\Core\Exception\InvalidResourceException;
use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
use ApiPlatform\Core\Routing\ResourcePathGeneratorInterface;
use Doctrine\Common\Inflector\Inflector;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\Loader;
Expand All @@ -35,12 +37,14 @@ final class ApiLoader extends Loader
private $fileLoader;
private $resourceNameCollectionFactory;
private $resourceMetadataFactory;
private $resourcePathGenerator;

public function __construct(KernelInterface $kernel, ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory)
public function __construct(KernelInterface $kernel, ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourcePathGeneratorInterface $resourcePathGenerator)
{
$this->fileLoader = new XmlFileLoader(new FileLocator($kernel->locateResource('@ApiPlatformBundle/Resources/config/routing')));
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->resourcePathGenerator = $resourcePathGenerator;
}

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

foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
$normalizedShortName = Inflector::pluralize(Inflector::tableize($resourceMetadata->getShortName()));
$resourceShortName = $resourceMetadata->getShortName();

if (null === $resourceShortName) {
throw new InvalidResourceException(sprintf('Resource %s has no short name defined.', $resourceClass));
}

foreach ($resourceMetadata->getCollectionOperations() as $operationName => $operation) {
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $normalizedShortName, true);
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $resourceShortName, true);
}

foreach ($resourceMetadata->getItemOperations() as $operationName => $operation) {
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $normalizedShortName, false);
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $resourceShortName, false);
}
}

Expand All @@ -84,12 +92,12 @@ public function supports($resource, $type = null)
* @param string $resourceClass
* @param string $operationName
* @param array $operation
* @param string $normalizedShortName
* @param string $resourceShortName
* @param bool $collection
*
* @throws RuntimeException
*/
private function addRoute(RouteCollection $routeCollection, string $resourceClass, string $operationName, array $operation, string $normalizedShortName, bool $collection)
private function addRoute(RouteCollection $routeCollection, string $resourceClass, string $operationName, array $operation, string $resourceShortName, bool $collection)
{
if (isset($operation['route_name'])) {
return;
Expand All @@ -113,14 +121,15 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas
$path = $operation['path'] ?? null;

if (null === $path) {
$path = '/'.$normalizedShortName;
$path = '/'.$this->resourcePathGenerator->generateResourceBasePath($resourceShortName);

if (!$collection) {
$path .= '/{id}';
}
}

$routeName = sprintf('%s%s_%s', self::ROUTE_NAME_PREFIX, $normalizedShortName, $actionName);
$resourceRouteName = Inflector::pluralize(Inflector::tableize($resourceShortName));
$routeName = sprintf('%s%s_%s', self::ROUTE_NAME_PREFIX, $resourceRouteName, $actionName);

$route = new Route(
$path,
Expand Down
21 changes: 21 additions & 0 deletions src/Exception/InvalidResourceException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Exception;

/**
* Invalid resource exception.
*
* @author Paul Le Corre <paul@lecorre.me>
*/
class InvalidResourceException extends \Exception implements ExceptionInterface
{
}
27 changes: 27 additions & 0 deletions src/Routing/DashResourcePathGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Routing;

use Doctrine\Common\Inflector\Inflector;

/**
* @author Paul Le Corre <paul@lecorre.me>
*/
class DashResourcePathGenerator implements ResourcePathGeneratorInterface
{
public function generateResourceBasePath(string $resourceShortName) : string
{
$pathName = strtolower(preg_replace('~(?<=\\w)([A-Z])~', '-$1', $resourceShortName));

return Inflector::pluralize($pathName);
}
}
20 changes: 20 additions & 0 deletions src/Routing/ResourcePathGeneratorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Routing;

/**
* @author Paul Le Corre <paul@lecorre.me>
*/
interface ResourcePathGeneratorInterface
{
public function generateResourceBasePath(string $resourceShortName) : string;
}
27 changes: 27 additions & 0 deletions src/Routing/UnderscoreResourcePathGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Routing;

use Doctrine\Common\Inflector\Inflector;

/**
* @author Paul Le Corre <paul@lecorre.me>
*/
class UnderscoreResourcePathGenerator implements ResourcePathGeneratorInterface
{
public function generateResourceBasePath(string $resourceShortName) : string
{
$pathName = Inflector::tableize($resourceShortName);

return Inflector::pluralize($pathName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ private function getContainerBuilderProphecy()
'api_platform.route_loader',
'api_platform.router',
'api_platform.iri_converter',
'api_platform.routing.resource_path_generator.underscore',
'api_platform.routing.resource_path_generator.dash',
'api_platform.listener.request.format',
'api_platform.listener.view.validation',
'api_platform.listener.request.format',
Expand Down Expand Up @@ -324,6 +326,7 @@ private function getContainerBuilderProphecy()
}

$aliases = [
'api_platform.routing.resource_path_generator' => 'api_platform.routing.resource_path_generator.underscore',
'api_platform.metadata.resource.name_collection_factory' => 'api_platform.metadata.resource.name_collection_factory.annotation',
'api_platform.metadata.resource.cache' => 'api_platform.metadata.resource.cache.array',
'api_platform.metadata.property.cache' => 'api_platform.metadata.property.cache.array',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public function testDefaultConfig()
'title' => 'title',
'description' => 'description',
'supported_formats' => ['jsonld' => ['mime_types' => ['application/ld+json']]],
'routing' => [
'resource_path_generator' => 'api_platform.routing.resource_path_generator.underscore',
],
'name_converter' => null,
'enable_fos_user' => false,
'enable_nelmio_api_doc' => true,
Expand Down
16 changes: 15 additions & 1 deletion tests/Bridge/Symfony/Routing/ApiLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\Metadata\Resource\ResourceNameCollection;
use ApiPlatform\Core\Routing\ResourcePathGeneratorInterface;
use ApiPlatform\Core\Tests\Fixtures\DummyEntity;
use Prophecy\Argument;
use Symfony\Component\HttpKernel\KernelInterface;
Expand Down Expand Up @@ -85,6 +86,8 @@ public function testApiLoader()
public function testNoMethodApiLoader()
{
$resourceMetadata = new ResourceMetadata();
$resourceMetadata = $resourceMetadata->withShortName('dummy');

$resourceMetadata = $resourceMetadata->withItemOperations([
'get' => [],
]);
Expand All @@ -96,6 +99,14 @@ public function testNoMethodApiLoader()
$routeCollection = $this->getApiLoaderWithResourceMetadata($resourceMetadata)->load(null);
}

/**
* @expectedException \ApiPlatform\Core\Exception\InvalidResourceException
*/
public function testNoShortNameApiLoader()
{
$this->getApiLoaderWithResourceMetadata(new ResourceMetadata())->load(null);
}

private function getApiLoaderWithResourceMetadata(ResourceMetadata $resourceMetadata): ApiLoader
{
$routingConfig = __DIR__.'/../../../../src/Bridge/Symfony/Bundle/Resources/config/routing';
Expand All @@ -109,7 +120,10 @@ private function getApiLoaderWithResourceMetadata(ResourceMetadata $resourceMeta
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
$resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([DummyEntity::class]));

$apiLoader = new ApiLoader($kernelProphecy->reveal(), $resourceNameCollectionFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal());
$resourcePathGeneratorProphecy = $this->prophesize(ResourcePathGeneratorInterface::class);
$resourcePathGeneratorProphecy->generateResourceBasePath('dummy')->willReturn('dummies');

$apiLoader = new ApiLoader($kernelProphecy->reveal(), $resourceNameCollectionFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), $resourcePathGeneratorProphecy->reveal());

return $apiLoader;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/Routing/DashResourcePathGeneratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Tests\Routing;

use ApiPlatform\Core\Routing\DashResourcePathGenerator;

class DashResourcePathGeneratorTest extends \PHPUnit_Framework_TestCase
{
public function testGenerateResourceBasePath()
{
$dashResourcePathGenerator = new DashResourcePathGenerator();

$this->assertSame('short-names', $dashResourcePathGenerator->generateResourceBasePath('ShortName'));
}
}
24 changes: 24 additions & 0 deletions tests/Routing/UnderscoreResourcePathGeneratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ApiPlatform\Core\Tests\Routing;

use ApiPlatform\Core\Routing\UnderscoreResourcePathGenerator;

class UnderscoreResourcePathGeneratorTest extends \PHPUnit_Framework_TestCase
{
public function testGenerateResourceBasePath()
{
$underscoreResourcePathGenerator = new UnderscoreResourcePathGenerator();

$this->assertSame('short_names', $underscoreResourcePathGenerator->generateResourceBasePath('ShortName'));
}
}