Passed
Pull Request — master (#321)
by Christoffer
04:21 queued 01:06
created

ObjectType   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 125
rs 10
c 0
b 0
f 0
wmc 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A hasInterfaces() 0 3 1
A getInterfaces() 0 6 2
A __construct() 0 16 1
A isTypeOf() 0 5 2
A buildInterfaces() 0 11 2
A hasIsTypeOfCallback() 0 3 1
A getIsTypeOfCallback() 0 3 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\NameAwareInterface;
9
use Digia\GraphQL\Language\Node\ObjectTypeDefinitionNode;
10
use Digia\GraphQL\Language\Node\ObjectTypeExtensionNode;
11
use React\Promise\PromiseInterface;
12
use function Digia\GraphQL\Type\resolveThunk;
13
14
/**
15
 * Object Type Definition
16
 *
17
 * Almost all of the GraphQL types you define will be object types. Object types
18
 * have a name, but most importantly describe their fields.
19
 *
20
 * Example:
21
 *
22
 *     $AddressType = newObjectType([
23
 *       'name'   => 'Address',
24
 *       'fields' => [
25
 *         'street'    => ['type' => stringType()],
26
 *         'number'    => ['type' => intType()],
27
 *         'formatted' => [
28
 *           'type'    => stringType(),
29
 *           'resolve' => function ($obj) {
30
 *             return $obj->number . ' ' . $obj->street
31
 *           }
32
 *         ]
33
 *       ]
34
 *     ]);
35
 *
36
 * When two types need to refer to each other, or a type needs to refer to
37
 * itself in a field, you can use a function expression (aka a closure or a
38
 * thunk) to supply the fields lazily.
39
 *
40
 * Example:
41
 *
42
 *     $PersonType = newObjectType([
43
 *       'name' => 'Person',
44
 *       'fields' => function () {
45
 *         return [
46
 *           'name'       => ['type' => stringType()],
47
 *           'bestFriend' => ['type' => $PersonType],
48
 *         ];
49
 *       }
50
 *     ]);
51
 */
52
class ObjectType implements NamedTypeInterface, CompositeTypeInterface, OutputTypeInterface,
53
    ASTNodeAwareInterface, DescriptionAwareInterface, FieldsAwareInterface
54
{
55
    use NameTrait;
56
    use DescriptionTrait;
57
    use FieldsTrait;
58
    use ResolveTrait;
59
    use ASTNodeTrait;
60
    use ExtensionASTNodesTrait;
61
62
    /**
63
     * @var callable
64
     */
65
    protected $isTypeOfCallback;
66
67
    /**
68
     * Interfaces can be defined either as an array or as a thunk.
69
     * Using thunks allows for cross-referencing of interfaces.
70
     *
71
     * @var array|callable
72
     */
73
    protected $interfacesOrThunk;
74
75
    /**
76
     * A list of interface instances.
77
     *
78
     * @var InterfaceType[]|null
79
     */
80
    protected $interfaces;
81
82
    /**
83
     * ObjectType constructor.
84
     *
85
     * @param string                        $name
86
     * @param null|string                   $description
87
     * @param array|callable                $fieldsOrThunk
88
     * @param array|callable                $interfacesOrThunk
89
     * @param callable|null                 $isTypeOfCallback
90
     * @param ObjectTypeDefinitionNode|null $astNode
91
     * @param ObjectTypeExtensionNode[]     $extensionASTNodes
92
     */
93
    public function __construct(
94
        string $name,
95
        ?string $description,
96
        $fieldsOrThunk,
97
        $interfacesOrThunk,
98
        ?callable $isTypeOfCallback,
99
        ?ObjectTypeDefinitionNode $astNode,
100
        array $extensionASTNodes
101
    ) {
102
        $this->name              = $name;
103
        $this->description       = $description;
104
        $this->rawFieldsOrThunk  = $fieldsOrThunk;
105
        $this->interfacesOrThunk = $interfacesOrThunk;
106
        $this->isTypeOfCallback  = $isTypeOfCallback;
107
        $this->astNode           = $astNode;
108
        $this->extensionAstNodes = $extensionASTNodes;
109
    }
110
111
    /**
112
     * @param mixed $value
113
     * @param mixed $context
114
     * @param mixed $info
115
     * @return bool|PromiseInterface
116
     */
117
    public function isTypeOf($value, $context, $info)
118
    {
119
        return null !== $this->isTypeOfCallback
120
            ? \call_user_func($this->isTypeOfCallback, $value, $context, $info)
121
            : false;
122
    }
123
124
    /**
125
     * @return bool
126
     * @throws InvariantException
127
     */
128
    public function hasInterfaces(): bool
129
    {
130
        return !empty($this->getInterfaces());
131
    }
132
133
    /**
134
     * @return InterfaceType[]
135
     * @throws InvariantException
136
     */
137
    public function getInterfaces(): array
138
    {
139
        if ($this->interfaces === null) {
140
            $this->interfaces = $this->buildInterfaces($this->interfacesOrThunk);
141
        }
142
        return $this->interfaces;
143
    }
144
145
    /**
146
     * @return bool
147
     */
148
    public function hasIsTypeOfCallback(): bool
149
    {
150
        return null !== $this->isTypeOfCallback;
151
    }
152
153
    /**
154
     * @return null|callable
155
     */
156
    public function getIsTypeOfCallback(): ?callable
157
    {
158
        return $this->isTypeOfCallback;
159
    }
160
161
    /**
162
     * @param array|callable $interfacesOrThunk
163
     * @return array
164
     * @throws InvariantException
165
     */
166
    protected function buildInterfaces($interfacesOrThunk): array
167
    {
168
        $interfaces = resolveThunk($interfacesOrThunk);
169
170
        if (!\is_array($interfaces)) {
171
            throw new InvariantException(
172
                \sprintf('%s interfaces must be an array or a function which returns an array.', $this->name)
173
            );
174
        }
175
176
        return $interfaces;
177
    }
178
}
179