Passed
Pull Request — master (#2959)
by Alan
04:43
created

SerializerContextBuilder::create()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 1
b 0
f 0
nc 12
nop 4
dl 0
loc 22
rs 9.5555
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
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\GraphQl\Serializer;
15
16
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
17
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
18
use GraphQL\Type\Definition\ResolveInfo;
19
20
/**
21
 * Builds the context used by the Symfony Serializer.
22
 *
23
 * @experimental
24
 *
25
 * @author Alan Poulain <[email protected]>
26
 */
27
final class SerializerContextBuilder implements SerializerContextBuilderInterface
28
{
29
    private $resourceMetadataFactory;
30
31
    public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory)
32
    {
33
        $this->resourceMetadataFactory = $resourceMetadataFactory;
34
    }
35
36
    public function create(?string $resourceClass, string $operationName, array $resolverContext, bool $normalization): array
37
    {
38
        $resourceMetadata = $resourceClass ? $this->resourceMetadataFactory->create($resourceClass) : null;
39
40
        $context = [
41
            'resource_class' => $resourceClass,
42
            'graphql_operation_name' => $operationName,
43
        ];
44
45
        if ($normalization) {
46
            $context['attributes'] = $this->fieldsToAttributes($resourceMetadata, $resolverContext);
47
        }
48
49
        if ($resourceMetadata) {
50
            $context['input'] = $resourceMetadata->getGraphqlAttribute($operationName, 'input', null, true);
51
            $context['output'] = $resourceMetadata->getGraphqlAttribute($operationName, 'output', null, true);
52
53
            $key = $normalization ? 'normalization_context' : 'denormalization_context';
54
            $context = array_merge($resourceMetadata->getGraphqlAttribute($operationName, $key, [], true), $context);
55
        }
56
57
        return $context;
58
    }
59
60
    /**
61
     * 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).
62
     */
63
    private function fieldsToAttributes(?ResourceMetadata $resourceMetadata, array $context): array
64
    {
65
        /** @var ResolveInfo $info */
66
        $info = $context['info'];
67
        $fields = $info->getFieldSelection(PHP_INT_MAX);
68
69
        $attributes = $this->replaceIdKeys($fields['edges']['node'] ?? $fields);
70
71
        if ($context['is_mutation']) {
72
            if (!$resourceMetadata) {
73
                throw new \LogicException('ResourceMetadata should always exist for a mutation.');
74
            }
75
76
            $wrapFieldName = lcfirst($resourceMetadata->getShortName());
77
78
            return $attributes[$wrapFieldName] ?? [];
79
        }
80
81
        return $attributes;
82
    }
83
84
    private function replaceIdKeys(array $fields): array
85
    {
86
        foreach ($fields as $key => $value) {
87
            if ('_id' === $key) {
88
                $fields['id'] = $fields['_id'];
89
                unset($fields['_id']);
90
            } elseif (\is_array($fields[$key])) {
91
                $fields[$key] = $this->replaceIdKeys($fields[$key]);
92
            }
93
        }
94
95
        return $fields;
96
    }
97
}
98