Completed
Pull Request — master (#201)
by Christoffer
02:54
created

EnumType::afterConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
3
namespace Digia\GraphQL\Type\Definition;
4
5
use Digia\GraphQL\Error\InvariantException;
6
use Digia\GraphQL\Language\Node\ASTNodeAwareInterface;
7
use Digia\GraphQL\Language\Node\ASTNodeTrait;
8
use Digia\GraphQL\Language\Node\EnumTypeDefinitionNode;
9
use Digia\GraphQL\Language\Node\EnumValueNode;
10
use Digia\GraphQL\Language\Node\NodeInterface;
11
use function Digia\GraphQL\Type\isAssocArray;
12
use function Digia\GraphQL\Util\invariant;
13
use function Digia\GraphQL\Util\toString;
14
15
/**
16
 * Enum Type Definition
17
 *
18
 * Some leaf values of requests and input values are Enums. GraphQL serializes
19
 * Enum values as strings, however internally Enums can be represented by any
20
 * kind of type, often integers.
21
 *
22
 * Example:
23
 *
24
 *     $RGBType = new GraphQLEnumType([
25
 *       'name'   => 'RGB',
26
 *       'values' => [
27
 *         'RED'   => ['value' => 0],
28
 *         'GREEN' => ['value' => 1],
29
 *         'BLUE'  => ['value' => 2]
30
 *       ]
31
 *     ]);
32
 *
33
 * Note: If a value is not provided in a definition, the name of the enum value
34
 * will be used as its internal value.
35
 */
36
class EnumType implements TypeInterface, NamedTypeInterface, InputTypeInterface, LeafTypeInterface,
37
    OutputTypeInterface, ASTNodeAwareInterface
38
{
39
    use NameTrait;
40
    use DescriptionTrait;
41
    use ASTNodeTrait;
42
43
    /**
44
     * Values can be defined either as an array or as a thunk.
45
     * Using thunks allows for cross-referencing of values.
46
     *
47
     * @var array
48
     */
49
    protected $valueMap;
50
51
    /**
52
     * A list of enum value instances.
53
     *
54
     * @var EnumValue[]
55
     */
56
    protected $values;
57
58
    /**
59
     * EnumType constructor.
60
     *
61
     * @param string                      $name
62
     * @param null|string                 $description
63
     * @param EnumValue[]                 $values
64
     * @param EnumTypeDefinitionNode|null $astNode
65
     * @throws InvariantException
66
     */
67
    public function __construct(string $name, ?string $description, array $values, ?EnumTypeDefinitionNode $astNode)
68
    {
69
        $this->name        = $name;
70
        $this->description = $description;
71
        $this->astNode     = $astNode;
72
        $this->valueMap    = $values;
73
74
        invariant(null !== $this->getName(), 'Must provide name.');
75
    }
76
77
    /**
78
     * @param $value
79
     * @return null|string
80
     * @throws InvariantException
81
     */
82
    public function serialize($value)
83
    {
84
        $enumValue = $this->getValueByValue($value);
85
86
        if ($enumValue) {
87
            return $enumValue->getName();
88
        }
89
90
        return null;
91
    }
92
93
    /**
94
     * @param $value
95
     * @return mixed|null
96
     * @throws InvariantException
97
     */
98
    public function parseValue($value)
99
    {
100
        if (\is_string($value)) {
101
            $enumValue = $this->getValueByName($value);
102
103
            if ($enumValue !== null) {
104
                return $enumValue->getValue();
105
            }
106
        }
107
108
        return null;
109
    }
110
111
    /**
112
     * @param NodeInterface $node
113
     * @return mixed|null
114
     * @throws InvariantException
115
     */
116
    public function parseLiteral(NodeInterface $node)
117
    {
118
        if ($node instanceof EnumValueNode) {
119
            $enumValue = $this->getValueByName($node->getValue());
120
121
            if ($enumValue !== null) {
122
                return $enumValue->getValue();
123
            }
124
        }
125
126
        return null;
127
    }
128
129
    /**
130
     * @param string $name
131
     * @return EnumValue|null
132
     * @throws InvariantException
133
     */
134
    public function getValue(string $name): ?EnumValue
135
    {
136
        return $this->getValueByName($name);
137
    }
138
139
    /**
140
     * @return EnumValue[]
141
     * @throws InvariantException
142
     */
143
    public function getValues(): array
144
    {
145
        if (!isset($this->values)) {
146
            $this->values = $this->buildValues($this->valueMap);
147
        }
148
        return $this->values;
149
    }
150
151
    /**
152
     * @param array $valueMap
153
     * @return $this
154
     */
155
    protected function setValues(array $valueMap): EnumType
156
    {
157
        $this->valueMap = $valueMap;
158
        return $this;
159
    }
160
161
    /**
162
     * @param string $name
163
     * @return EnumValue|null
164
     * @throws InvariantException
165
     */
166
    protected function getValueByName(string $name): ?EnumValue
167
    {
168
        foreach ($this->getValues() as $enumValue) {
169
            if ($enumValue->getName() === $name) {
170
                return $enumValue;
171
            }
172
        }
173
174
        return null;
175
    }
176
177
    /**
178
     * @param mixed $value
179
     * @return EnumValue|null
180
     * @throws InvariantException
181
     */
182
    protected function getValueByValue($value): ?EnumValue
183
    {
184
        foreach ($this->getValues() as $enumValue) {
185
            if ($enumValue->getValue() === $value) {
186
                return $enumValue;
187
            }
188
        }
189
190
        return null;
191
    }
192
193
    /**
194
     * @param array $valueMap
195
     * @return array
196
     * @throws InvariantException
197
     */
198
    protected function buildValues(array $valueMap): array
199
    {
200
        invariant(
201
            isAssocArray($valueMap),
202
            \sprintf('%s values must be an associative array with value names as keys.', $this->getName())
203
        );
204
205
        $values = [];
206
207
        foreach ($valueMap as $valueName => $valueConfig) {
208
            invariant(
209
                isAssocArray($valueConfig),
210
                \sprintf(
211
                    '%s.%s must refer to an object with a "value" key representing an internal value but got: %s',
212
                    $this->getName(),
213
                    $valueName,
214
                    toString($valueConfig)
215
                )
216
            );
217
218
            invariant(
219
                !isset($valueConfig['isDeprecated']),
220
                \sprintf(
221
                    '%s.%s should provided "deprecationReason" instead of "isDeprecated".',
222
                    $this->getName(),
223
                    $valueName
224
                )
225
            );
226
227
            $values[] = new EnumValue(
228
                $valueName,
229
                $valueConfig['description'] ?? null,
230
                $valueConfig['deprecationReason'] ?? null,
231
                $valueConfig['astNode'] ?? null,
232
                $valueConfig['value'] ?? null
233
            );
234
        }
235
236
        return $values;
237
    }
238
}
239