Skip to content

Commit 636d989

Browse files
author
abluchet
committed
Allow subresource items in the iri converter
1 parent d805e9a commit 636d989

File tree

10 files changed

+306
-414
lines changed

10 files changed

+306
-414
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
<argument type="service" id="api_platform.router" />
5959
<argument type="service" id="api_platform.property_accessor" />
6060
<argument type="service" id="api_platform.identifiers_extractor.cached" />
61-
<argument type="service" id="api_platform.identifier.denormalizer" />
61+
<argument type="service" id="api_platform.operation_data_provider" />
6262
</service>
6363
<service id="ApiPlatform\Core\Api\IriConverterInterface" alias="api_platform.iri_converter" />
6464

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
<argument type="tagged" tag="api_platform.subresource_data_provider" />
2727
</service>
2828
<service id="ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface" alias="api_platform.subresource_data_provider" />
29+
30+
<service id="api_platform.operation_data_provider" class="ApiPlatform\Core\DataProvider\OperationDataProvider">
31+
<argument type="service" id="api_platform.collection_data_provider" />
32+
<argument type="service" id="api_platform.item_data_provider" />
33+
<argument type="service" id="api_platform.subresource_data_provider" />
34+
<argument type="service" id="api_platform.identifier.denormalizer" />
35+
</service>
2936
</services>
3037

3138
</container>

src/Bridge/Symfony/Routing/IriConverter.php

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
use ApiPlatform\Core\Api\OperationType;
2020
use ApiPlatform\Core\Api\UrlGeneratorInterface;
2121
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
22+
use ApiPlatform\Core\DataProvider\OperationDataProvider;
2223
use ApiPlatform\Core\Exception\InvalidArgumentException;
2324
use ApiPlatform\Core\Exception\ItemNotFoundException;
2425
use ApiPlatform\Core\Exception\RuntimeException;
25-
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;
2626
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2727
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2828
use ApiPlatform\Core\Util\ClassInfoTrait;
@@ -44,24 +44,23 @@ final class IriConverter implements IriConverterInterface
4444
private $routeNameResolver;
4545
private $router;
4646
private $identifiersExtractor;
47-
private $identifierDenormalizer;
47+
private $operationDataProvider;
4848

49-
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, ChainIdentifierDenormalizer $identifierDenormalizer = null)
49+
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ItemDataProviderInterface $itemDataProvider, RouteNameResolverInterface $routeNameResolver, RouterInterface $router, PropertyAccessorInterface $propertyAccessor = null, IdentifiersExtractorInterface $identifiersExtractor = null, OperationDataProvider $operationDataProvider = null)
5050
{
5151
$this->itemDataProvider = $itemDataProvider;
5252
$this->routeNameResolver = $routeNameResolver;
5353
$this->router = $router;
54-
$this->identifierDenormalizer = $identifierDenormalizer;
54+
$this->identifiersExtractor = $identifiersExtractor;
55+
$this->operationDataProvider = $operationDataProvider;
5556

5657
if (null === $identifiersExtractor) {
5758
@trigger_error('Not injecting ItemIdentifiersExtractor is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3', E_USER_DEPRECATED);
5859
$this->identifiersExtractor = new IdentifiersExtractor($propertyNameCollectionFactory, $propertyMetadataFactory, $propertyAccessor ?? PropertyAccess::createPropertyAccessor());
59-
} else {
60-
$this->identifiersExtractor = $identifiersExtractor;
6160
}
6261

63-
if (null === $identifierDenormalizer) {
64-
@trigger_error(sprintf('Not injecting "%s" is deprecated since API Platform 2.2 and will not be possible anymore in API Platform 3.', ChainIdentifierDenormalizer::class), E_USER_DEPRECATED);
62+
if (null === $operationDataProvider) {
63+
@trigger_error(sprintf('Not injecting "%s" is deprecated since API Platform 2.2 and will not be possible anymore in API Platform 3.', OperationDataProvider::class), E_USER_DEPRECATED);
6564
}
6665
}
6766

@@ -76,18 +75,20 @@ public function getItemFromIri(string $iri, array $context = [])
7675
throw new InvalidArgumentException(sprintf('No route matches "%s".', $iri), $e->getCode(), $e);
7776
}
7877

79-
if (!isset($parameters['_api_resource_class'], $parameters['id'])) {
80-
throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
81-
}
78+
if ($this->operationDataProvider) {
79+
if ($item = $this->operationDataProvider->getDataFromRouteParameters($iri, $parameters, $context)) {
80+
return $item;
81+
}
8282

83-
$identifiers = $parameters['id'];
83+
throw new ItemNotFoundException(sprintf('Item not found for "%s".', $iri));
84+
}
8485

85-
if ($this->identifierDenormalizer) {
86-
$identifiers = $this->identifierDenormalizer->denormalize((string) $parameters['id'], $parameters['_api_resource_class']);
87-
$context[ChainIdentifierDenormalizer::HAS_IDENTIFIER_DENORMALIZER] = true;
86+
// Legacy code below
87+
if (!isset($parameters['_api_resource_class']) || !isset($parameters['id'])) {
88+
throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
8889
}
8990

90-
if ($item = $this->itemDataProvider->getItem($parameters['_api_resource_class'], $identifiers, $parameters['_api_item_operation_name'] ?? null, $context)) {
91+
if ($item = $this->itemDataProvider->getItem($parameters['_api_resource_class'], $parameters['id'], $parameters['_api_item_operation_name'] ?? null, $context)) {
9192
return $item;
9293
}
9394

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\DataProvider;
15+
16+
use ApiPlatform\Core\Exception\InvalidArgumentException;
17+
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;
18+
use ApiPlatform\Core\Util\RequestAttributesExtractor;
19+
20+
/**
21+
* @internal
22+
*/
23+
class OperationDataProvider
24+
{
25+
use OperationDataProviderTrait;
26+
27+
public function __construct(CollectionDataProviderInterface $collectionDataProvider, ItemDataProviderInterface $itemDataProvider, SubresourceDataProviderInterface $subresourceDataProvider, ChainIdentifierDenormalizer $identifierDenormalizer)
28+
{
29+
$this->collectionDataProvider = $collectionDataProvider;
30+
$this->itemDataProvider = $itemDataProvider;
31+
$this->subresourceDataProvider = $subresourceDataProvider;
32+
$this->identifierDenormalizer = $identifierDenormalizer;
33+
}
34+
35+
public function getDataFromRouteParameters(string $iri = null, array $parameters, array $context = [])
36+
{
37+
if (!isset($parameters['_api_resource_class'])) {
38+
throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
39+
}
40+
41+
$attributes = RequestAttributesExtractor::extractAttributesFromParameters($parameters);
42+
$identifiers = $this->extractIdentifiers($parameters, $attributes);
43+
$context[ChainIdentifierDenormalizer::HAS_IDENTIFIER_DENORMALIZER] = true;
44+
45+
if (isset($attributes['subresource_operation_name'])) {
46+
return $this->getSubresourceData($identifiers, $attributes, $context);
47+
}
48+
49+
return $this->getItemData($identifiers, $attributes, $context);
50+
}
51+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\DataProvider;
15+
16+
use ApiPlatform\Core\Exception\InvalidIdentifierException;
17+
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;
18+
19+
trait OperationDataProviderTrait
20+
{
21+
/**
22+
* @var CollectionDataProviderInterface
23+
*/
24+
private $collectionDataProvider;
25+
/**
26+
* @var ItemDataProviderInterface
27+
*/
28+
private $itemDataProvider;
29+
/**
30+
* @var SubresourceDataProviderInterface
31+
*/
32+
private $subresourceDataProvider;
33+
/**
34+
* @var ChainIdentifierDenormalizer
35+
*/
36+
private $identifierDenormalizer;
37+
38+
/**
39+
* Retrieves data for a collection operation.
40+
*
41+
* @return array|\Traversable|null
42+
*/
43+
private function getCollectionData(array $attributes, array $context)
44+
{
45+
return $this->collectionDataProvider->getCollection($attributes['resource_class'], $attributes['collection_operation_name'], $context);
46+
}
47+
48+
/**
49+
* Gets data for an item operation.
50+
*
51+
* @throws NotFoundHttpException
52+
*
53+
* @return object|null
54+
*/
55+
private function getItemData($identifiers, array $attributes, array $context)
56+
{
57+
return $this->itemDataProvider->getItem($attributes['resource_class'], $identifiers, $attributes['item_operation_name'], $context);
58+
}
59+
60+
/**
61+
* Gets data for a nested operation.
62+
*
63+
* @throws NotFoundHttpException
64+
* @throws RuntimeException
65+
*
66+
* @return object|null
67+
*/
68+
private function getSubresourceData($identifiers, array $attributes, array $context)
69+
{
70+
return $this->subresourceDataProvider->getSubresource($attributes['resource_class'], $identifiers, $attributes['subresource_context'] + $context, $attributes['subresource_operation_name']);
71+
}
72+
73+
/**
74+
* @param array $parameters - usually comes from $request->attributes->all()
75+
*
76+
* @throws InvalidIdentifierException
77+
*/
78+
private function extractIdentifiers(array $parameters, array $attributes)
79+
{
80+
if (isset($attributes['item_operation_name'])) {
81+
$id = $parameters['id'];
82+
83+
if ($this->identifierDenormalizer) {
84+
return $this->identifierDenormalizer->denormalize((string) $id, $attributes['resource_class']);
85+
}
86+
87+
return $id;
88+
}
89+
90+
$identifiers = [];
91+
92+
foreach ($attributes['subresource_context']['identifiers'] as $key => list($id, $resourceClass, $hasIdentifier)) {
93+
if (false === $hasIdentifier) {
94+
continue;
95+
}
96+
97+
$identifiers[$id] = $parameters[$id];
98+
99+
if ($this->identifierDenormalizer) {
100+
$identifiers[$id] = $this->identifierDenormalizer->denormalize((string) $identifiers[$id], $resourceClass);
101+
}
102+
}
103+
104+
return $identifiers;
105+
}
106+
}

0 commit comments

Comments
 (0)