Issues (158)

src/NodeParser/QueryNodeParser.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jerowork\GraphqlAttributeSchema\NodeParser;
6
7
use Generator;
8
use Jerowork\GraphqlAttributeSchema\Attribute\Query;
9
use Jerowork\GraphqlAttributeSchema\Node\QueryNode;
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 QueryNodeParser implements NodeParser
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_READONLY, expecting T_CLASS on line 22 at column 6
Loading history...
23
{
24
    use RetrieveNameForFieldTrait;
25
    use GetAttributeTrait;
26
27
    private const array ALLOWED_SCALAR_TYPES_FOR_DEFERRED_TYPE_LOADER = ['string', 'int', 'array'];
28
29 9
    public function __construct(
30
        private TypeReferenceDecider $typeReferenceDecider,
31
        private MethodArgumentsNodeParser $methodArgumentsNodeParser,
32 9
    ) {}
33
34 7
    #[Override]
35
    public function parse(string $attribute, ReflectionClass $class, ?ReflectionMethod $method): Generator
36
    {
37 7
        if ($attribute !== Query::class) {
38 3
            return;
39
        }
40
41 6
        if ($method === null) {
42
            throw new ParseException('Logic: Missing ReflectionMethod');
43
        }
44
45 6
        $attribute = $this->getAttribute($method, Query::class);
46 6
        $returnType = $method->getReturnType();
47
48 6
        $reference = $this->typeReferenceDecider->getTypeReference($returnType, $attribute);
49
50 6
        if ($reference === null) {
51 1
            throw ParseException::invalidReturnType($class->getName(), $method->getName());
52
        }
53
54
        // When reference is ConnectionType, the query needs to have Connection as return type
55 5
        if ($reference instanceof ConnectionTypeReference) {
56 3
            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 4
        if ($attribute->deferredTypeLoader !== null) {
63 3
            if ($returnType === null) {
64
                throw ParseException::missingDeferredTypeLoaderReturnType($class->getName(), $method->getName());
65
            }
66
67 3
            if ($returnType instanceof ReflectionNamedType
68 3
                && $returnType->isBuiltin()
69 3
                && !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 3
            if (!$returnType instanceof Stringable) {
75
                throw ParseException::invalidDeferredTypeLoaderReturnType($class->getName(), $method->getName());
76
            }
77
        }
78
79 4
        yield new QueryNode(
80 4
            $class->getName(),
81 4
            $this->retrieveNameForField($method, $attribute),
82 4
            $attribute->description,
83 4
            array_values([...$this->methodArgumentsNodeParser->parse($method)]),
84 4
            $reference,
85 4
            $method->getName(),
86 4
            $attribute->deprecationReason,
87 4
            $attribute->deferredTypeLoader,
88 4
        );
89
    }
90
}
91