Passed
Pull Request — master (#201)
by Christoffer
02:32
created

EnumType::getValueByValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 4
nc 3
nop 1
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 newEnumType([
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): ?string
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 string $name
153
     * @return EnumValue|null
154
     * @throws InvariantException
155
     */
156
    protected function getValueByName(string $name): ?EnumValue
157
    {
158
        foreach ($this->getValues() as $enumValue) {
159
            if ($enumValue->getName() === $name) {
160
                return $enumValue;
161
            }
162
        }
163
164
        return null;
165
    }
166
167
    /**
168
     * @param mixed $value
169
     * @return EnumValue|null
170
     * @throws InvariantException
171
     */
172
    protected function getValueByValue($value): ?EnumValue
173
    {
174
        foreach ($this->getValues() as $enumValue) {
175
            if ($enumValue->getValue() === $value) {
176
                return $enumValue;
177
            }
178
        }
179
180
        return null;
181
    }
182
183
    /**
184
     * @param array $valueMap
185
     * @return array
186
     * @throws InvariantException
187
     */
188
    protected function buildValues(array $valueMap): array
189
    {
190
        invariant(
191
            isAssocArray($valueMap),
192
            \sprintf('%s values must be an associative array with value names as keys.', $this->getName())
193
        );
194
195
        $values = [];
196
197
        foreach ($valueMap as $valueName => $valueConfig) {
198
            invariant(
199
                isAssocArray($valueConfig),
200
                \sprintf(
201
                    '%s.%s must refer to an object with a "value" key representing an internal value but got: %s',
202
                    $this->name,
203
                    $valueName,
204
                    toString($valueConfig)
205
                )
206
            );
207
208
            invariant(
209
                !isset($valueConfig['isDeprecated']),
210
                \sprintf(
211
                    '%s.%s should provided "deprecationReason" instead of "isDeprecated".',
212
                    $this->name,
213
                    $valueName
214
                )
215
            );
216
217
            $values[] = new EnumValue(
218
                $valueName,
219
                $valueConfig['description'] ?? null,
220
                $valueConfig['deprecationReason'] ?? null,
221
                $valueConfig['astNode'] ?? null,
222
                $valueConfig['value'] ?? null
223
            );
224
        }
225
226
        return $values;
227
    }
228
}
229