1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Jerowork\GraphqlAttributeSchema\NodeParser; |
||
6 | |||
7 | use Generator; |
||
8 | use Jerowork\GraphqlAttributeSchema\Attribute\Mutation; |
||
9 | use Jerowork\GraphqlAttributeSchema\Node\MutationNode; |
||
10 | use Jerowork\GraphqlAttributeSchema\Node\TypeReference\ConnectionTypeReference; |
||
11 | use Jerowork\GraphqlAttributeSchema\NodeParser\Child\MethodArgumentsNodeParser; |
||
12 | use Jerowork\GraphqlAttributeSchema\Type\Connection\Connection; |
||
13 | use Override; |
||
14 | use ReflectionClass; |
||
15 | use ReflectionMethod; |
||
16 | use ReflectionNamedType; |
||
17 | use Stringable; |
||
18 | |||
19 | /** |
||
20 | * @internal |
||
21 | */ |
||
22 | final readonly class MutationNodeParser implements NodeParser |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
23 | { |
||
24 | use RetrieveNameForFieldTrait; |
||
25 | use GetAttributeTrait; |
||
26 | |||
27 | private const array ALLOWED_SCALAR_TYPES_FOR_DEFERRED_TYPE_LOADER = ['string', 'int', 'array']; |
||
28 | |||
29 | 8 | public function __construct( |
|
30 | private TypeReferenceDecider $typeReferenceDecider, |
||
31 | private MethodArgumentsNodeParser $methodArgumentsNodeParser, |
||
32 | 8 | ) {} |
|
33 | |||
34 | 6 | #[Override] |
|
35 | public function parse(string $attribute, ReflectionClass $class, ?ReflectionMethod $method): Generator |
||
36 | { |
||
37 | 6 | if ($attribute !== Mutation::class) { |
|
38 | 3 | return; |
|
39 | } |
||
40 | |||
41 | 5 | if ($method === null) { |
|
42 | throw new ParseException('Logic: Missing ReflectionMethod'); |
||
43 | } |
||
44 | |||
45 | 5 | $attribute = $this->getAttribute($method, Mutation::class); |
|
46 | 5 | $returnType = $method->getReturnType(); |
|
47 | |||
48 | 5 | $reference = $this->typeReferenceDecider->getTypeReference($returnType, $attribute); |
|
49 | |||
50 | 5 | if ($reference === null) { |
|
51 | 1 | throw ParseException::invalidReturnType($class->getName(), $method->getName()); |
|
52 | } |
||
53 | |||
54 | // When reference is ConnectionType, the mutation needs to have Connection as return type |
||
55 | 4 | if ($reference instanceof ConnectionTypeReference) { |
|
56 | 1 | if (!$returnType instanceof ReflectionNamedType || $returnType->getName() !== Connection::class) { |
|
57 | 1 | throw ParseException::invalidConnectionReturnType($class->getName(), $method->getName()); |
|
58 | } |
||
59 | } |
||
60 | |||
61 | // When it has a deferred type loader, the return type needs to be an integer, string or Stringable |
||
62 | 3 | if ($attribute->deferredTypeLoader !== null) { |
|
63 | if ($returnType === null) { |
||
64 | throw ParseException::missingDeferredTypeLoaderReturnType($class->getName(), $method->getName()); |
||
65 | } |
||
66 | |||
67 | if ($returnType instanceof ReflectionNamedType |
||
68 | && $returnType->isBuiltin() |
||
69 | && !in_array($returnType->getName(), self::ALLOWED_SCALAR_TYPES_FOR_DEFERRED_TYPE_LOADER, true) |
||
70 | ) { |
||
71 | throw ParseException::invalidDeferredTypeLoaderReturnType($class->getName(), $method->getName()); |
||
72 | } |
||
73 | |||
74 | if (!$returnType instanceof Stringable) { |
||
75 | throw ParseException::invalidDeferredTypeLoaderReturnType($class->getName(), $method->getName()); |
||
76 | } |
||
77 | } |
||
78 | |||
79 | 3 | yield new MutationNode( |
|
80 | 3 | $class->getName(), |
|
81 | 3 | $this->retrieveNameForField($method, $attribute), |
|
82 | 3 | $attribute->description, |
|
83 | 3 | array_values([...$this->methodArgumentsNodeParser->parse($method)]), |
|
84 | 3 | $reference, |
|
85 | 3 | $method->getName(), |
|
86 | 3 | $attribute->deprecationReason, |
|
87 | 3 | $attribute->deferredTypeLoader, |
|
88 | 3 | ); |
|
89 | } |
||
90 | } |
||
91 |