Skip to content

Commit 0f8509c

Browse files
committed
graphql ok
1 parent aa33674 commit 0f8509c

32 files changed

+570
-137
lines changed

src/Api/IdentifiersExtractor.php

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,154 @@
1313

1414
namespace ApiPlatform\Api;
1515

16-
class_exists(\ApiPlatform\Metadata\IdentifiersExtractor::class);
16+
use ApiPlatform\Metadata\Exception\RuntimeException;
17+
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
18+
use ApiPlatform\Metadata\HttpOperation;
19+
use ApiPlatform\Metadata\Operation;
20+
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
21+
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
22+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
23+
use ApiPlatform\Metadata\Util\ResourceClassInfoTrait;
24+
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
25+
use Symfony\Component\PropertyAccess\PropertyAccess;
26+
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
1727

18-
if (false) {
19-
final class IdentifiersExtractor extends \ApiPlatform\Metadata\IdentifiersExtractor
28+
/**
29+
* {@inheritdoc}
30+
*
31+
* @deprecated use ApiPlatform\Metadata\IdentifiersExtractor instead
32+
*
33+
* @author Antoine Bluchet <[email protected]>
34+
*/
35+
final class IdentifiersExtractor implements IdentifiersExtractorInterface
36+
{
37+
use ResourceClassInfoTrait;
38+
private readonly PropertyAccessorInterface $propertyAccessor;
39+
40+
public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, PropertyAccessorInterface $propertyAccessor = null)
41+
{
42+
$this->resourceMetadataFactory = $resourceMetadataFactory;
43+
$this->resourceClassResolver = $resourceClassResolver;
44+
$this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*
50+
* TODO: 3.0 identifiers should be stringable?
51+
*/
52+
public function getIdentifiersFromItem(object $item, Operation $operation = null, array $context = []): array
53+
{
54+
if (!$this->isResourceClass($this->getObjectClass($item))) {
55+
return ['id' => $this->propertyAccessor->getValue($item, 'id')];
56+
}
57+
58+
if ($operation && $operation->getClass()) {
59+
return $this->getIdentifiersFromOperation($item, $operation, $context);
60+
}
61+
62+
$resourceClass = $this->getResourceClass($item, true);
63+
$operation ??= $this->resourceMetadataFactory->create($resourceClass)->getOperation(null, false, true);
64+
65+
return $this->getIdentifiersFromOperation($item, $operation, $context);
66+
}
67+
68+
private function getIdentifiersFromOperation(object $item, Operation $operation, array $context = []): array
69+
{
70+
if ($operation instanceof HttpOperation) {
71+
$links = $operation->getUriVariables();
72+
} elseif ($operation instanceof GraphQlOperation) {
73+
$links = $operation->getLinks();
74+
}
75+
76+
$identifiers = [];
77+
foreach ($links ?? [] as $link) {
78+
if (1 < (is_countable($link->getIdentifiers()) ? \count($link->getIdentifiers()) : 0)) {
79+
$compositeIdentifiers = [];
80+
foreach ($link->getIdentifiers() as $identifier) {
81+
$compositeIdentifiers[$identifier] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $identifier, $link->getParameterName());
82+
}
83+
84+
$identifiers[$link->getParameterName()] = CompositeIdentifierParser::stringify($compositeIdentifiers);
85+
continue;
86+
}
87+
88+
$parameterName = $link->getParameterName();
89+
$identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $link->getIdentifiers()[0], $parameterName, $link->getToProperty());
90+
}
91+
92+
return $identifiers;
93+
}
94+
95+
/**
96+
* Gets the value of the given class property.
97+
*/
98+
private function getIdentifierValue(object $item, string $class, string $property, string $parameterName, string $toProperty = null): float|bool|int|string
2099
{
100+
if ($item instanceof $class) {
101+
try {
102+
return $this->resolveIdentifierValue($this->propertyAccessor->getValue($item, $property), $parameterName);
103+
} catch (NoSuchPropertyException $e) {
104+
throw new RuntimeException('Not able to retrieve identifiers.', $e->getCode(), $e);
105+
}
106+
}
107+
108+
if ($toProperty) {
109+
return $this->resolveIdentifierValue($this->propertyAccessor->getValue($item, "$toProperty.$property"), $parameterName);
110+
}
111+
112+
$resourceClass = $this->getResourceClass($item, true);
113+
foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
114+
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
115+
116+
$types = $propertyMetadata->getBuiltinTypes();
117+
if (null === ($type = $types[0] ?? null)) {
118+
continue;
119+
}
120+
121+
try {
122+
if ($type->isCollection()) {
123+
$collectionValueType = $type->getCollectionValueTypes()[0] ?? null;
124+
125+
if (null !== $collectionValueType && $collectionValueType->getClassName() === $class) {
126+
return $this->resolveIdentifierValue($this->propertyAccessor->getValue($item, sprintf('%s[0].%s', $propertyName, $property)), $parameterName);
127+
}
128+
}
129+
130+
if ($type->getClassName() === $class) {
131+
return $this->resolveIdentifierValue($this->propertyAccessor->getValue($item, "$propertyName.$property"), $parameterName);
132+
}
133+
} catch (NoSuchPropertyException $e) {
134+
throw new RuntimeException('Not able to retrieve identifiers.', $e->getCode(), $e);
135+
}
136+
}
137+
138+
throw new RuntimeException('Not able to retrieve identifiers.');
139+
}
140+
141+
/**
142+
* TODO: in 3.0 this method just uses $identifierValue instanceof \Stringable and we remove the weird behavior.
143+
*
144+
* @param mixed|\Stringable $identifierValue
145+
*/
146+
private function resolveIdentifierValue(mixed $identifierValue, string $parameterName): float|bool|int|string
147+
{
148+
if (null === $identifierValue) {
149+
throw new RuntimeException('No identifier value found, did you forget to persist the entity?');
150+
}
151+
152+
if (\is_scalar($identifierValue)) {
153+
return $identifierValue;
154+
}
155+
156+
if ($identifierValue instanceof \Stringable) {
157+
return (string) $identifierValue;
158+
}
159+
160+
if ($identifierValue instanceof \BackedEnum) {
161+
return (string) $identifierValue->value;
162+
}
163+
164+
throw new RuntimeException(sprintf('We were not able to resolve the identifier matching parameter "%s".', $parameterName));
21165
}
22166
}

src/Api/IdentifiersExtractorInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
namespace ApiPlatform\Api;
1515

16-
class_alias(\ApiPlatform\Metadata\IdentifiersExtractorInterface::class, \ApiPlatform\Api\IdentifiersExtractorInterface::class);
16+
class_exists(\ApiPlatform\Metadata\IdentifiersExtractorInterface::class);
1717

18-
if (false) {
18+
if (!class_exists(IdentifiersExtractorInterface::class)) {
1919
interface IdentifiersExtractorInterface extends \ApiPlatform\Metadata\IdentifiersExtractorInterface
2020
{
2121
}

src/Api/IriConverterInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
namespace ApiPlatform\Api;
1515

16-
class_alias(\ApiPlatform\Metadata\IriConverterInterface::class, \ApiPlatform\Api\IriConverterInterface::class);
16+
class_exists(\ApiPlatform\Metadata\IriConverterInterface::class);
1717

18-
if (false) {
18+
if (!class_exists(IriConverterInterface::class)) {
1919
interface IriConverterInterface extends \ApiPlatform\Metadata\IriConverterInterface
2020
{
2121
}

src/Api/ResourceClassResolverInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
namespace ApiPlatform\Api;
1515

16-
class_alias(\ApiPlatform\Metadata\ResourceClassResolverInterface::class, \ApiPlatform\Api\ResourceClassResolverInterface::class);
16+
class_exists(\ApiPlatform\Metadata\ResourceClassResolverInterface::class);
1717

1818
if (false) {
19-
interface ResourceClassResolverInterface extends \ApiPlatform\Metadata\ResourceClassResolverInterface {}
19+
interface ResourceClassResolverInterface extends \ApiPlatform\Metadata\ResourceClassResolverInterface
20+
{
21+
}
2022
}

src/Api/UriVariablesConverterInterface.php

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,10 @@
1313

1414
namespace ApiPlatform\Api;
1515

16-
use ApiPlatform\Exception\InvalidIdentifierException;
16+
class_exists(\ApiPlatform\Metadata\UriVariablesConverterInterface::class);
1717

18-
/**
19-
* Identifier converter.
20-
*
21-
* @author Antoine Bluchet <[email protected]>
22-
*/
23-
interface UriVariablesConverterInterface
24-
{
25-
/**
26-
* Takes an array of strings representing URI variables (identifiers) and transform their values to the expected type.
27-
*
28-
* @param array $data URI variables to convert to PHP values
29-
* @param string $class The class to which the URI variables belong to
30-
*
31-
* @throws InvalidIdentifierException
32-
*
33-
* @return array Array indexed by identifiers properties with their values denormalized
34-
*/
35-
public function convert(array $data, string $class, array $context = []): array;
18+
if (!class_exists(UriVariablesConverterInterface::class)) {
19+
interface UriVariablesConverterInterface extends \ApiPlatform\Metadata\UriVariablesConverterInterface
20+
{
21+
}
3622
}

src/Elasticsearch/composer.json

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,82 @@
11
{
2-
"name": "api-platform/elasticseach",
3-
"description": "Elasticsearch support",
4-
"type": "library",
5-
"keywords": [
6-
"Filter",
7-
"Elasticsearch"
8-
],
9-
"homepage": "https://api-platform.com",
10-
"license": "MIT",
11-
"authors": [
12-
{
13-
"name": "Kévin Dunglas",
14-
"email": "[email protected]",
15-
"homepage": "https://dunglas.fr"
2+
"name": "api-platform/elasticseach",
3+
"description": "Elasticsearch support",
4+
"type": "library",
5+
"keywords": [
6+
"Filter",
7+
"Elasticsearch"
8+
],
9+
"homepage": "https://api-platform.com",
10+
"license": "MIT",
11+
"authors": [
12+
{
13+
"name": "Kévin Dunglas",
14+
"email": "[email protected]",
15+
"homepage": "https://dunglas.fr"
16+
},
17+
{
18+
"name": "API Platform Community",
19+
"homepage": "https://api-platform.com/community/contributors"
20+
}
21+
],
22+
"require": {
23+
"php": ">=8.1",
24+
"api-platform/metadata": "*@dev || ^3.1",
25+
"api-platform/state": "*@dev || ^3.1",
26+
"api-platform/serializer": "*@dev || ^3.1",
27+
"elasticsearch/elasticsearch": "^7.11.0",
28+
"symfony/cache": "^6.1",
29+
"symfony/console": "^6.2",
30+
"symfony/property-info": "^6.1",
31+
"symfony/serializer": "^6.1",
32+
"symfony/uid": "^6.1",
33+
"symfony/property-access": "^6.1"
1634
},
17-
{
18-
"name": "API Platform Community",
19-
"homepage": "https://api-platform.com/community/contributors"
20-
}
21-
],
22-
"require": {
23-
"php": ">=8.1",
24-
"api-platform/metadata": "*@dev || ^3.1",
25-
"api-platform/state": "*@dev || ^3.1",
26-
"elasticsearch/elasticsearch": "^7.11.0",
27-
"symfony/cache": "^6.1",
28-
"symfony/console": "^6.2",
29-
"symfony/property-info": "^6.1",
30-
"symfony/serializer": "^6.1",
31-
"symfony/uid": "^6.1",
32-
"symfony/property-access": "^6.1"
33-
},
34-
"conflict": {
35-
"elasticsearch/elasticsearch": ">=8.0"
36-
},
37-
"require-dev": {
38-
"phpspec/prophecy-phpunit": "^2.0",
39-
"symfony/phpunit-bridge": "^6.1"
40-
},
41-
"autoload": {
42-
"psr-4": {
43-
"ApiPlatform\\Elasticsearch\\": ""
35+
"conflict": {
36+
"elasticsearch/elasticsearch": ">=8.0"
4437
},
45-
"exclude-from-classmap": [
46-
"/Tests/"
47-
]
48-
},
49-
"config": {
50-
"preferred-install": {
51-
"*": "dist"
38+
"require-dev": {
39+
"phpspec/prophecy-phpunit": "^2.0",
40+
"symfony/phpunit-bridge": "^6.1"
41+
},
42+
"autoload": {
43+
"psr-4": {
44+
"ApiPlatform\\Elasticsearch\\": ""
45+
},
46+
"exclude-from-classmap": [
47+
"/Tests/"
48+
]
5249
},
53-
"sort-packages": true,
54-
"allow-plugins": {
55-
"composer/package-versions-deprecated": true,
56-
"phpstan/extension-installer": true
57-
}
58-
},
59-
"extra": {
60-
"branch-alias": {
61-
"dev-main": "3.2.x-dev"
50+
"config": {
51+
"preferred-install": {
52+
"*": "dist"
53+
},
54+
"sort-packages": true,
55+
"allow-plugins": {
56+
"composer/package-versions-deprecated": true,
57+
"phpstan/extension-installer": true
58+
}
6259
},
63-
"symfony": {
64-
"require": "^6.1"
65-
}
66-
},
67-
"repositories": [
68-
{
69-
"type": "path",
70-
"url": "../Metadata"
60+
"extra": {
61+
"branch-alias": {
62+
"dev-main": "3.2.x-dev"
63+
},
64+
"symfony": {
65+
"require": "^6.1"
66+
}
7167
},
72-
{
73-
"type": "path",
74-
"url": "../State"
75-
}
76-
]
68+
"repositories": [
69+
{
70+
"type": "path",
71+
"url": "../Metadata"
72+
},
73+
{
74+
"type": "path",
75+
"url": "../State"
76+
},
77+
{
78+
"type": "path",
79+
"url": "../Serializer"
80+
}
81+
]
7782
}

src/Exception/InvalidArgumentException.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313

1414
namespace ApiPlatform\Exception;
1515

16+
use ApiPlatform\Metadata\Exception\InvalidArgumentException as MetadataInvalidArgumentException;
17+
1618
/**
1719
* Invalid argument exception.
1820
*
1921
* @author Kévin Dunglas <[email protected]>
22+
*
23+
* @deprecated use \ApiPlatform\Metadata\Exception\InvalidArgumentException
2024
*/
21-
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
25+
class InvalidArgumentException extends MetadataInvalidArgumentException
2226
{
2327
}

0 commit comments

Comments
 (0)