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

overblog / GraphQLBundle / 10437663729

01 Jul 2024 01:58PM UTC coverage: 98.364%. Remained the same
10437663729

push

github

web-flow
Merge pull request #1189 from jerowork/master

Support Symfony 7.1

4330 of 4402 relevant lines covered (98.36%)

40.0 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);
97✔
28
        $this->doctrineMapping = $doctrineMapping;
97✔
29
    }
97✔
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;
52✔
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)) {
53✔
47
            throw new TypeGuessingException(sprintf('You must install doctrine/orm package to use this type guesser.'));
×
48
        }
49

50
        if (!$reflector instanceof ReflectionProperty) {
53✔
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);
53✔
56

57
        if (null !== $columnAnnotation) {
53✔
58
            $type = $this->resolveTypeFromDoctrineType($columnAnnotation->type ?: 'string');
52✔
59
            $nullable = $columnAnnotation->nullable;
52✔
60
            if ($type) {
52✔
61
                return $nullable ? $type : sprintf('%s!', $type);
52✔
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 = [
53✔
68
            OneToMany::class => true,
69
            OneToOne::class => false,
70
            ManyToMany::class => true,
71
            ManyToOne::class => false,
72
        ];
73

74
        foreach ($associationAnnotations as $associationClass => $isMultiple) {
53✔
75
            /** @var OneToMany|OneToOne|ManyToMany|ManyToOne|null $associationAnnotation */
76
            $associationAnnotation = $this->getAnnotation($reflector, $associationClass);
53✔
77
            if (null !== $associationAnnotation) {
53✔
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
                    } else {
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
                } 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();
53✔
106
        $annotations = [];
53✔
107
        switch (true) {
108
            case $reflector instanceof ReflectionClass: $annotations = $reader->getClassAnnotations($reflector);
53✔
109
                break;
×
110
            case $reflector instanceof ReflectionMethod: $annotations = $reader->getMethodAnnotations($reflector);
53✔
111
                break;
×
112
            case $reflector instanceof ReflectionProperty: $annotations = $reader->getPropertyAnnotations($reflector);
53✔
113
                break;
53✔
114
        }
115
        foreach ($annotations as $annotation) {
53✔
116
            if ($annotation instanceof $annotationClass) {
52✔
117
                /** @var MappingAnnotation $annotation */
118
                return $annotation;
52✔
119
            }
120
        }
121

122
        return null;
53✔
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) {
52✔
133
            return $namespace.'\\'.$className;
52✔
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])) {
52✔
145
            return $this->doctrineMapping[$doctrineType];
52✔
146
        }
147

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