Skip to content

Commit 602bfa6

Browse files
authored
Merge pull request #279 from thecodingmachine/webonyx_type_proxy
Adding back the possibility to inject pure Webonyx objects in GraphQLite
2 parents 1cc83ff + 8b7628d commit 602bfa6

File tree

11 files changed

+556
-15
lines changed

11 files changed

+556
-15
lines changed

docs/custom_types.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ services:
8989
9090
### Other frameworks
9191
92-
The easiest way is to use a `StaticTypeMapper`. This class is used to register custom output types.
92+
The easiest way is to use a `StaticTypeMapper`. Use this class to register custom output types.
9393

9494
```php
9595
// Sample code:
@@ -105,11 +105,10 @@ $staticTypeMapper->setNotMappedTypes([
105105
new MyCustomOutputType()
106106
]);
107107
108+
// Register the static type mapper in your application using the SchemaFactory instance
109+
$schemaFactory->addTypeMapper($staticTypeMapper);
108110
```
109111

110-
The `StaticTypeMapper` instance MUST be registered in your container and linked to a `CompositeTypeMapper`
111-
that will aggregate all the type mappers of the application.
112-
113112
## Registering a custom scalar type (advanced)
114113

115114
If you need to add custom scalar types, first, check the [GraphQLite Misc. Types library](https://github.com/thecodingmachine/graphqlite-misc-types).

phpcs.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<exclude-pattern>tests/dependencies/*</exclude-pattern>
1717
<exclude-pattern>src/Utils/NamespacedCache.php</exclude-pattern>
1818
<exclude-pattern>src/Types/TypeAnnotatedInterfaceType.php</exclude-pattern>
19+
<exclude-pattern>src/Mappers/Proxys/*</exclude-pattern>
1920

2021
<!-- Excluding ServiceResolver.php and QueryFieldDescriptor.php because Slevomat 5.0 does not support array shaped. -->
2122
<!-- Support is added in 5.1 so we can remove these excludes when Slevomat 5.1 is out -->

phpstan.neon

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ parameters:
99
-
1010
message: '#Method TheCodingMachine\\GraphQLite\\Types\\Mutable(Interface|Object)Type::getFields\(\) should return array<GraphQL\\Type\\Definition\\FieldDefinition> but returns array\|float\|int#'
1111
path: src/Types/MutableTrait.php
12+
-
13+
message: '#Method TheCodingMachine\\GraphQLite\\Mappers\\Proxys\\Mutable(Interface|Object)TypeAdapter::getFields\(\) should return array<GraphQL\\Type\\Definition\\FieldDefinition> but returns array\|float\|int#'
14+
path: src/Mappers/Proxys/MutableAdapterTrait.php
1215
#- "#Parameter \\#2 \\$inputTypeNode of static method GraphQL\\\\Utils\\\\AST::typeFromAST() expects GraphQL\\\\Language\\\\AST\\\\ListTypeNode\\|GraphQL\\\\Language\\\\AST\\\\NamedTypeNode\\|GraphQL\\\\Language\\\\AST\\\\NonNullTypeNode, GraphQL\\\\Language\\\\AST\\\\ListTypeNode\\|GraphQL\\\\Language\\\\AST\\\\NameNode\\|GraphQL\\\\Language\\\\AST\\\\NonNullTypeNode given.#"
1316
- "#PHPDoc tag @throws with type Psr\\\\SimpleCache\\\\InvalidArgumentException is not subtype of Throwable#"
1417
- '#Variable \$context might not be defined.#'
@@ -21,6 +24,9 @@ parameters:
2124
-
2225
message: '#Parameter \#2 \$subType of method .* expects#'
2326
path: src/Mappers/Root/IteratorTypeMapper.php
27+
-
28+
message: '#Method TheCodingMachine\\GraphQLite\\Mappers\\StaticTypeMapper::castOutputTypeToMutable() should return TheCodingMachine\\GraphQLite\\Mappers\\Proxys\\MutableInterfaceTypeAdapter\|TheCodingMachine\\GraphQLite\\Mappers\\Proxys\\MutableObjectTypeAdapter but returns GraphQL\\Type\\Definition\\InterfaceType|GraphQL\\Type\\Definition\\ObjectType.#'
29+
path: src/Mappers/StaticTypeMapper.php
2430
-
2531
message: '#Unreachable statement - code above always terminates.#'
2632
path: src/Http/WebonyxGraphqlMiddleware.php
@@ -31,8 +37,7 @@ parameters:
3137
message: '#Method TheCodingMachine\\GraphQLite\\AnnotationReader::getMethodAnnotations\(\) should return array<int, T of object> but returns array<object>.#'
3238
path: src/AnnotationReader.php
3339
- '#Call to an undefined method GraphQL\\Error\\ClientAware::getMessage()#'
34-
# Needed because of a bug in PHP-CS
35-
- '#PHPDoc tag @param for parameter \$args with type mixed is not subtype of native type array<int, mixed>.#'
40+
3641

3742
#-
3843
# message: '#If condition is always true#'
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\GraphQLite\Mappers\Proxys;
5+
6+
7+
use Exception;
8+
use GraphQL\Error\InvariantViolation;
9+
use GraphQL\Type\Definition\FieldDefinition;
10+
use GraphQL\Type\Definition\InterfaceType;
11+
use GraphQL\Type\Definition\ObjectType;
12+
use GraphQL\Type\Definition\ResolveInfo;
13+
use RuntimeException;
14+
use TheCodingMachine\GraphQLite\Types\MutableInterface;
15+
use TheCodingMachine\GraphQLite\Types\NoFieldsException;
16+
use function get_class;
17+
18+
/**
19+
* @internal
20+
*/
21+
trait MutableAdapterTrait
22+
{
23+
/**
24+
* @var ObjectType|InterfaceType
25+
*/
26+
private $type;
27+
/**
28+
* @var string|null
29+
*/
30+
private $className;
31+
32+
/** @var string */
33+
private $status = MutableInterface::STATUS_PENDING;
34+
35+
/** @var array<callable> */
36+
private $fieldsCallables = [];
37+
38+
/** @var FieldDefinition[]|null */
39+
private $finalFields;
40+
41+
/**
42+
* @throws InvariantViolation
43+
*/
44+
public function assertValid(): void
45+
{
46+
$this->type->assertValid();
47+
}
48+
49+
/**
50+
* @return string
51+
*/
52+
public function jsonSerialize()
53+
{
54+
return $this->type->jsonSerialize();
55+
}
56+
57+
/**
58+
* @return string
59+
*/
60+
public function toString()
61+
{
62+
return $this->type->toString();
63+
}
64+
65+
/**
66+
* @return string
67+
*/
68+
public function __toString()
69+
{
70+
return $this->type->__toString();
71+
}
72+
73+
74+
/**
75+
* @param string $name
76+
*
77+
* @return FieldDefinition
78+
*
79+
* @throws Exception
80+
*/
81+
public function getField($name): FieldDefinition
82+
{
83+
if ($this->status === MutableInterface::STATUS_PENDING) {
84+
throw new RuntimeException('You must freeze() a '.get_class($this).' before fetching its fields.');
85+
}
86+
87+
return $this->type->getField($name);
88+
}
89+
90+
/**
91+
* @param string $name
92+
*
93+
* @return bool
94+
*/
95+
public function hasField($name): bool
96+
{
97+
if ($this->status === MutableInterface::STATUS_PENDING) {
98+
throw new RuntimeException('You must freeze() a '.get_class($this).' before fetching its fields.');
99+
}
100+
101+
return $this->type->hasField($name);
102+
}
103+
104+
/**
105+
* @return FieldDefinition[]
106+
*
107+
* @throws InvariantViolation
108+
*/
109+
public function getFields(): array
110+
{
111+
if ($this->finalFields === null) {
112+
if ($this->status === MutableInterface::STATUS_PENDING) {
113+
throw new RuntimeException('You must freeze() a '.get_class($this).' before fetching its fields.');
114+
}
115+
116+
$this->finalFields = $this->type->getFields();
117+
foreach ($this->fieldsCallables as $fieldsCallable) {
118+
$this->finalFields = FieldDefinition::defineFieldMap($this, $fieldsCallable()) + $this->finalFields;
119+
}
120+
if (empty($this->finalFields)) {
121+
throw NoFieldsException::create($this->name);
122+
}
123+
}
124+
125+
return $this->finalFields;
126+
}
127+
128+
public function freeze(): void
129+
{
130+
$this->status = self::STATUS_FROZEN;
131+
}
132+
133+
public function getStatus(): string
134+
{
135+
return $this->status;
136+
}
137+
138+
public function addFields(callable $fields): void
139+
{
140+
if ($this->status !== MutableInterface::STATUS_PENDING) {
141+
throw new RuntimeException('Tried to add fields to a frozen MutableInterfaceType.');
142+
}
143+
$this->fieldsCallables[] = $fields;
144+
}
145+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\GraphQLite\Mappers\Proxys;
5+
6+
use Exception;
7+
use GraphQL\Error\InvariantViolation;
8+
use GraphQL\Type\Definition\FieldDefinition;
9+
use GraphQL\Type\Definition\InterfaceType;
10+
use GraphQL\Type\Definition\ObjectType;
11+
use GraphQL\Type\Definition\ResolveInfo;
12+
use GraphQL\Utils\Utils;
13+
use RuntimeException;
14+
use TheCodingMachine\GraphQLite\Types\MutableInterface;
15+
use TheCodingMachine\GraphQLite\Types\MutableInterfaceType;
16+
use TheCodingMachine\GraphQLite\Types\NoFieldsException;
17+
use function call_user_func;
18+
use function is_array;
19+
use function is_callable;
20+
use function is_string;
21+
use function sprintf;
22+
23+
/**
24+
* An adapter class (actually a proxy) that adds the "mutable" feature to any Webonyx ObjectType.
25+
*
26+
* @internal
27+
*/
28+
class MutableInterfaceTypeAdapter extends MutableInterfaceType implements MutableInterface
29+
{
30+
/** @use MutableAdapterTrait<InterfaceType> */
31+
use MutableAdapterTrait;
32+
33+
public function __construct(InterfaceType $type, ?string $className = null)
34+
{
35+
$this->type = $type;
36+
$this->className = $className;
37+
$this->name = $type->name;
38+
$this->config = $type->config;
39+
$this->astNode = $type->astNode;
40+
$this->extensionASTNodes = $type->extensionASTNodes;
41+
}
42+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\GraphQLite\Mappers\Proxys;
5+
6+
use Exception;
7+
use GraphQL\Error\InvariantViolation;
8+
use GraphQL\Type\Definition\FieldDefinition;
9+
use GraphQL\Type\Definition\InterfaceType;
10+
use GraphQL\Type\Definition\ObjectType;
11+
use GraphQL\Type\Definition\ResolveInfo;
12+
use GraphQL\Utils\Utils;
13+
use RuntimeException;
14+
use TheCodingMachine\GraphQLite\Types\MutableInterface;
15+
use TheCodingMachine\GraphQLite\Types\MutableObjectType;
16+
use TheCodingMachine\GraphQLite\Types\NoFieldsException;
17+
use function assert;
18+
use function call_user_func;
19+
use function is_array;
20+
use function is_callable;
21+
use function is_string;
22+
use function sprintf;
23+
24+
/**
25+
* An adapter class (actually a proxy) that adds the "mutable" feature to any Webonyx ObjectType.
26+
*
27+
* @internal
28+
*/
29+
class MutableObjectTypeAdapter extends MutableObjectType implements MutableInterface
30+
{
31+
/** @use MutableAdapterTrait<ObjectType> */
32+
use MutableAdapterTrait;
33+
34+
public function __construct(ObjectType $type, ?string $className = null)
35+
{
36+
$this->type = $type;
37+
$this->className = $className;
38+
$this->name = $type->name;
39+
$this->config = $type->config;
40+
$this->astNode = $type->astNode;
41+
$this->extensionASTNodes = $type->extensionASTNodes;
42+
$this->resolveFieldFn = $type->resolveFieldFn;
43+
}
44+
45+
/**
46+
* @return InterfaceType[]
47+
*/
48+
public function getInterfaces()
49+
{
50+
$type = $this->type;
51+
assert($type instanceof ObjectType);
52+
return $type->getInterfaces();
53+
}
54+
55+
/**
56+
* @param mixed[] $value
57+
* @param mixed[]|null $context
58+
*
59+
* @return bool|null
60+
*/
61+
public function isTypeOf($value, $context, ResolveInfo $info)
62+
{
63+
$type = $this->type;
64+
assert($type instanceof ObjectType);
65+
return $type->isTypeOf($value, $context, $info);
66+
}
67+
68+
/**
69+
* @param InterfaceType $iface
70+
*
71+
* @return bool
72+
*/
73+
public function implementsInterface($iface)
74+
{
75+
$type = $this->type;
76+
assert($type instanceof ObjectType);
77+
return $type->implementsInterface($iface);
78+
}
79+
}

0 commit comments

Comments
 (0)