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

overblog / GraphQLBundle / 7470162746

11 Dec 2023 02:32PM UTC coverage: 98.431%. Remained the same
7470162746

push

github

web-flow
Merge pull request #1144 from sparklink-pro/master

Symfony 7 / PHP 8.3 compatibility

4265 of 4333 relevant lines covered (98.43%)

39.1 hits per line

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

93.75
/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);
92✔
28
        $this->doctrineMapping = $doctrineMapping;
92✔
29
    }
92✔
30

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

36
    public function supports(Reflector $reflector): bool
37
    {
38
        return $reflector instanceof ReflectionProperty;
48✔
39
    }
40

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

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

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

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

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

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

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

93
                        return sprintf('%s%s', $type, $isNullable ? '' : '!');
48✔
94
                    }
95
                } else {
96
                    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✔
97
                }
98
            }
99
        }
100
        throw new TypeGuessingException(sprintf('No Doctrine ORM annotation found.'));
1✔
101
    }
102

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

122
        return null;
49✔
123
    }
124

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

136
        return $className;
×
137
    }
138

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

148
        switch ($doctrineType) {
149
            case 'integer':
48✔
150
            case 'smallint':
48✔
151
            case 'bigint':
48✔
152
                return 'Int';
48✔
153
            case 'string':
48✔
154
            case 'text':
48✔
155
                return 'String';
48✔
156
            case 'bool':
48✔
157
            case 'boolean':
48✔
158
                return 'Boolean';
48✔
159
            case 'float':
48✔
160
            case 'decimal':
48✔
161
                return 'Float';
48✔
162
            default:
163
                return null;
2✔
164
        }
165
    }
166
}
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