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

overblog / GraphQLBundle / 21577605669

29 Jan 2026 07:39AM UTC coverage: 98.546%. Remained the same
21577605669

push

github

web-flow
Test on PHP 8.5 (#1237)

* Test on PHP 8.5

* fix remove dummy attribute

4541 of 4608 relevant lines covered (98.55%)

76.57 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);
190✔
28
        $this->doctrineMapping = $doctrineMapping;
190✔
29
    }
30

31
    public function getName(): string
32
    {
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;
56✔
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)) {
54✔
48
            throw new TypeGuessingException(sprintf('You must install doctrine/orm package to use this type guesser.'));
×
49
        }
50

51
        if (!$reflector instanceof ReflectionProperty) {
54✔
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);
54✔
57

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

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

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

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

93
                    return sprintf('%s%s', $type, $isNullable ? '' : '!');
52✔
94
                }
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();
54✔
104
        $annotations = [];
54✔
105
        switch (true) {
106
            case $reflector instanceof ReflectionClass: $annotations = $reader->getClassAnnotations($reflector);
54✔
107
                break;
×
108
            case $reflector instanceof ReflectionMethod: $annotations = $reader->getMethodAnnotations($reflector);
54✔
109
                break;
×
110
            case $reflector instanceof ReflectionProperty: $annotations = $reader->getPropertyAnnotations($reflector);
54✔
111
                break;
54✔
112
        }
113
        foreach ($annotations as $annotation) {
54✔
114
            if ($annotation instanceof $annotationClass) {
52✔
115
                /** @var MappingAnnotation $annotation */
116
                return $annotation;
52✔
117
            }
118
        }
119

120
        return null;
54✔
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
    {
130
        if (!str_contains($className, '\\') && $namespace) {
52✔
131
            return $namespace.'\\'.$className;
52✔
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
    {
142
        if (isset($this->doctrineMapping[$doctrineType])) {
52✔
143
            return $this->doctrineMapping[$doctrineType];
52✔
144
        }
145

146
        switch ($doctrineType) {
147
            case 'integer':
52✔
148
            case 'smallint':
52✔
149
            case 'bigint':
52✔
150
                return 'Int';
52✔
151
            case 'string':
52✔
152
            case 'text':
52✔
153
                return 'String';
52✔
154
            case 'bool':
52✔
155
            case 'boolean':
52✔
156
                return 'Boolean';
52✔
157
            case 'float':
52✔
158
            case 'decimal':
52✔
159
                return 'Float';
52✔
160
            default:
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