-
-
Notifications
You must be signed in to change notification settings - Fork 969
Expand file tree
/
Copy pathSerializerContextBuilder.php
More file actions
118 lines (95 loc) · 4.85 KB
/
Copy pathSerializerContextBuilder.php
File metadata and controls
118 lines (95 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?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.
*/
declare(strict_types=1);
namespace ApiPlatform\GraphQl\Serializer;
use ApiPlatform\Metadata\GraphQl\Mutation;
use ApiPlatform\Metadata\GraphQl\Operation;
use ApiPlatform\Metadata\GraphQl\Subscription;
use GraphQL\Type\Definition\ResolveInfo;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
/**
* Builds the context used by the Symfony Serializer.
*
* @author Alan Poulain <contact@alanpoulain.eu>
*/
final class SerializerContextBuilder implements SerializerContextBuilderInterface
{
public function __construct(private readonly ?NameConverterInterface $nameConverter)
{
}
public function create(?string $resourceClass, Operation $operation, array $resolverContext, bool $normalization): array
{
$context = ['resource_class' => $resourceClass, 'operation_name' => $operation->getName(), 'graphql_operation_name' => $operation->getName()];
if (isset($resolverContext['fields'])) {
$context['no_resolver_data'] = true;
}
$context['operation'] = $operation;
$context['input'] = $operation->getInputClass() === $operation->getClass() ? null : ['class' => $operation->getInputClass()];
$context['output'] = $operation->getOutputClass() === $operation->getClass() ? null : ['class' => $operation->getOutputClass()];
// if (($inputClass = $operation->getInputClass()) !== $operation->getClass()) {
// $context['input'] = ['class' => $inputClass];
// }
// if (($outputClass = $operation->getOutputClass()) !== $operation->getClass()) {
// $context['output'] = ['class' => $outputClass];
// }
$context = $normalization ? array_merge($operation->getNormalizationContext() ?? [], $context) : array_merge($operation->getDenormalizationContext() ?? [], $context);
if ($normalization) {
$context['attributes'] = $this->fieldsToAttributes($resourceClass, $operation, $resolverContext, $context);
}
// to keep the cache computation smaller, we have "operation_name" and "iri" anyways
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'root_operation';
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'operation';
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'object';
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'data';
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'property_metadata';
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'circular_reference_limit_counters';
$context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'debug_trace_id';
return $context;
}
/**
* Retrieves fields, recursively replaces the "_id" key (the raw id) by "id" (the name of the property expected by the Serializer) and flattens edge and node structures (pagination).
*/
private function fieldsToAttributes(?string $resourceClass, Operation $operation, array $resolverContext, array $context): array
{
if (isset($resolverContext['fields'])) {
$fields = $resolverContext['fields'];
} else {
/** @var ResolveInfo $info */
$info = $resolverContext['info'];
$fields = $info->getFieldSelection(\PHP_INT_MAX);
}
$attributes = $this->replaceIdKeys($fields['edges']['node'] ?? $fields['collection'] ?? $fields, $resourceClass, $context);
if ($operation instanceof Subscription || $operation instanceof Mutation) {
$wrapFieldName = lcfirst($operation->getShortName());
return $attributes[$wrapFieldName] ?? [];
}
return $attributes;
}
private function replaceIdKeys(array $fields, ?string $resourceClass, array $context): array
{
$denormalizedFields = [];
foreach ($fields as $key => $value) {
if ('_id' === $key) {
$denormalizedFields['id'] = $fields['_id'];
continue;
}
$denormalizedFields[$this->denormalizePropertyName((string) $key, $resourceClass, $context)] = \is_array($value) ? $this->replaceIdKeys($value, $resourceClass, $context) : $value;
}
return $denormalizedFields;
}
private function denormalizePropertyName(string $property, ?string $resourceClass, array $context): string
{
if (null === $this->nameConverter) {
return $property;
}
return $this->nameConverter->denormalize($property, $resourceClass, null, $context);
}
}