• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

overblog / GraphQLBundle / 23208575639

17 Mar 2026 05:51PM UTC coverage: 98.553% (+0.007%) from 98.546%
23208575639

Pull #1241

github

web-flow
Merge 6e70843d8 into 7e9f36ef2
Pull Request #1241: resettable schemas, getCurrentSchemaName for TypeResolver, and IsPublic support for InputObject fields

44 of 44 new or added lines in 9 files covered. (100.0%)

53 existing lines in 9 files now uncovered.

4563 of 4630 relevant lines covered (98.55%)

78.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/src/Config/Parser/MetadataParser/TypeGuesser/DoctrineTypeGuesser.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Overblog\GraphQLBundle\Config\Parser\MetadataParser\TypeGuesser;
6

7
use Doctrine\ORM\Mapping\Annotation as MappingAnnotation;
8
use Doctrine\ORM\Mapping\Column;
9
use Doctrine\ORM\Mapping\JoinColumn;
10
use Doctrine\ORM\Mapping\ManyToMany;
11
use Doctrine\ORM\Mapping\ManyToOne;
12
use Doctrine\ORM\Mapping\OneToMany;
13
use Doctrine\ORM\Mapping\OneToOne;
14
use Overblog\GraphQLBundle\Config\Parser\AnnotationParser;
15
use Overblog\GraphQLBundle\Config\Parser\MetadataParser\ClassesTypesMap;
16
use ReflectionClass;
17
use ReflectionMethod;
18
use ReflectionProperty;
19
use Reflector;
20

21
final class DoctrineTypeGuesser extends TypeGuesser
22
{
23
    protected array $doctrineMapping = [];
24

25
    public function __construct(ClassesTypesMap $map, array $doctrineMapping = [])
26
    {
27
        parent::__construct($map);
198✔
28
        $this->doctrineMapping = $doctrineMapping;
198✔
29
    }
30

31
    public function getName(): string
32
    {
UNCOV
33
        return 'Doctrine annotations ';
4✔
34
    }
35

36
    public function supports(Reflector $reflector): bool
37
    {
38
        // If we are on doctrine/orm v2
39
        return class_exists(\Doctrine\ORM\Version::class) && $reflector instanceof ReflectionProperty;
60✔
40
    }
41

42
    /**
43
     * @param ReflectionProperty $reflector
44
     */
45
    public function guessType(ReflectionClass $reflectionClass, Reflector $reflector, array $filterGraphQLTypes = []): ?string
46
    {
47
        if (!class_exists(Column::class)) {
58✔
48
            throw new TypeGuessingException(sprintf('You must install doctrine/orm package to use this type guesser.'));
×
49
        }
50

51
        if (!$reflector instanceof ReflectionProperty) {
58✔
52
            throw new TypeGuessingException('Doctrine type guesser only apply to properties.');
2✔
53
        }
54

55
        /** @var Column|null $columnAnnotation */
56
        $columnAnnotation = $this->getAnnotation($reflector, Column::class);
58✔
57

58
        if (null !== $columnAnnotation) {
58✔
UNCOV
59
            $type = $this->resolveTypeFromDoctrineType($columnAnnotation->type ?: 'string');
56✔
UNCOV
60
            $nullable = $columnAnnotation->nullable;
56✔
UNCOV
61
            if ($type) {
56✔
UNCOV
62
                return $nullable ? $type : sprintf('%s!', $type);
56✔
63
            }
UNCOV
64
            throw new TypeGuessingException(sprintf('Unable to auto-guess GraphQL type from Doctrine type "%s"', $columnAnnotation->type));
2✔
65
        }
66

67
        $associationAnnotations = [
58✔
68
            OneToMany::class => true,
58✔
69
            OneToOne::class => false,
58✔
70
            ManyToMany::class => true,
58✔
71
            ManyToOne::class => false,
58✔
72
        ];
58✔
73

74
        foreach ($associationAnnotations as $associationClass => $isMultiple) {
58✔
75
            /** @var OneToMany|OneToOne|ManyToMany|ManyToOne|null $associationAnnotation */
76
            $associationAnnotation = $this->getAnnotation($reflector, $associationClass);
58✔
77
            if (null !== $associationAnnotation) {
58✔
UNCOV
78
                $target = $this->fullyQualifiedClassName($associationAnnotation->targetEntity, $reflectionClass->getNamespaceName());
56✔
UNCOV
79
                $type = $this->map->resolveType($target, ['type']);
56✔
80

UNCOV
81
                if ($type) {
56✔
UNCOV
82
                    $isMultiple = $associationAnnotations[$associationAnnotation::class];
56✔
UNCOV
83
                    if ($isMultiple) {
56✔
UNCOV
84
                        return sprintf('[%s]!', $type);
56✔
85
                    }
UNCOV
86
                    $isNullable = false;
56✔
87
                    /** @var JoinColumn|null $joinColumn */
UNCOV
88
                    $joinColumn = $this->getAnnotation($reflector, JoinColumn::class);
56✔
UNCOV
89
                    if (null !== $joinColumn) {
56✔
UNCOV
90
                        $isNullable = $joinColumn->nullable;
56✔
91
                    }
92

UNCOV
93
                    return sprintf('%s%s', $type, $isNullable ? '' : '!');
56✔
94
                }
UNCOV
95
                throw new TypeGuessingException(sprintf('Unable to auto-guess GraphQL type from Doctrine target class "%s" (check if the target class is a GraphQL type itself (with a @Metadata\Type metadata).', $target));
2✔
96
            }
97
        }
98
        throw new TypeGuessingException(sprintf('No Doctrine ORM annotation found.'));
2✔
99
    }
100

101
    private function getAnnotation(Reflector $reflector, string $annotationClass): ?MappingAnnotation
102
    {
103
        $reader = AnnotationParser::getAnnotationReader();
58✔
104
        $annotations = [];
58✔
105
        switch (true) {
106
            case $reflector instanceof ReflectionClass: $annotations = $reader->getClassAnnotations($reflector);
58✔
107
                break;
×
108
            case $reflector instanceof ReflectionMethod: $annotations = $reader->getMethodAnnotations($reflector);
58✔
109
                break;
×
110
            case $reflector instanceof ReflectionProperty: $annotations = $reader->getPropertyAnnotations($reflector);
58✔
111
                break;
58✔
112
        }
113
        foreach ($annotations as $annotation) {
58✔
UNCOV
114
            if ($annotation instanceof $annotationClass) {
56✔
115
                /** @var MappingAnnotation $annotation */
UNCOV
116
                return $annotation;
56✔
117
            }
118
        }
119

120
        return null;
58✔
121
    }
122

123
    /**
124
     * Resolve a FQN from classname and namespace.
125
     *
126
     * @internal
127
     */
128
    public function fullyQualifiedClassName(string $className, string $namespace): string
129
    {
UNCOV
130
        if (!str_contains($className, '\\') && $namespace) {
56✔
UNCOV
131
            return $namespace.'\\'.$className;
56✔
132
        }
133

134
        return $className;
×
135
    }
136

137
    /**
138
     * Resolve a GraphQLType from a doctrine type.
139
     */
140
    private function resolveTypeFromDoctrineType(string $doctrineType): ?string
141
    {
UNCOV
142
        if (isset($this->doctrineMapping[$doctrineType])) {
56✔
UNCOV
143
            return $this->doctrineMapping[$doctrineType];
56✔
144
        }
145

146
        switch ($doctrineType) {
UNCOV
147
            case 'integer':
56✔
UNCOV
148
            case 'smallint':
56✔
UNCOV
149
            case 'bigint':
56✔
UNCOV
150
                return 'Int';
56✔
UNCOV
151
            case 'string':
56✔
UNCOV
152
            case 'text':
56✔
UNCOV
153
                return 'String';
56✔
UNCOV
154
            case 'bool':
56✔
UNCOV
155
            case 'boolean':
56✔
UNCOV
156
                return 'Boolean';
56✔
UNCOV
157
            case 'float':
56✔
UNCOV
158
            case 'decimal':
56✔
UNCOV
159
                return 'Float';
56✔
160
            default:
UNCOV
161
                return null;
2✔
162
        }
163
    }
164
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc