1
|
|
|
<?php |
2
|
|
|
namespace Arthem\GraphQLMapper\Mapping\Guesser; |
3
|
|
|
|
4
|
|
|
use Arthem\GraphQLMapper\Mapping\Context\ContainerContext; |
5
|
|
|
use Arthem\GraphQLMapper\Mapping\Context\FieldContext; |
6
|
|
|
use Arthem\GraphQLMapper\Mapping\Field; |
7
|
|
|
use Arthem\GraphQLMapper\Mapping\Guesser\Guess\Guess; |
8
|
|
|
use Arthem\GraphQLMapper\Mapping\Guesser\Guess\TypeGuess; |
9
|
|
|
use Arthem\GraphQLMapper\Mapping\SchemaContainer; |
10
|
|
|
use Arthem\GraphQLMapper\Mapping\Type; |
11
|
|
|
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; |
12
|
|
|
use Doctrine\Common\Persistence\Mapping\MappingException; |
13
|
|
|
use Doctrine\Common\Persistence\ObjectManager; |
14
|
|
|
use Doctrine\DBAL\Types\Type as DoctrineType; |
15
|
|
|
use Doctrine\ORM\Mapping\ClassMetadataInfo; |
16
|
|
|
|
17
|
|
|
class DoctrineGuesser implements TypeResolveGuesserInterface, FieldResolveGuesserInterface, FieldTypeGuesserInterface |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* @var ClassMetadataFactory |
21
|
|
|
*/ |
22
|
|
|
private $metadataFactory; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @param ObjectManager $objectManager |
26
|
|
|
*/ |
27
|
|
|
public function __construct(ObjectManager $objectManager) |
28
|
|
|
{ |
29
|
|
|
$this->metadataFactory = $objectManager->getMetadataFactory(); |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* {@inheritdoc} |
34
|
|
|
*/ |
35
|
|
|
public function guessFieldType(FieldContext $fieldContext) |
36
|
|
|
{ |
37
|
|
|
if (!$this->isFieldContainerSupported($fieldContext)) { |
38
|
|
|
return; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** @var Type $fieldContainer */ |
42
|
|
|
$fieldContainer = $fieldContext->getContainer(); |
43
|
|
|
$model = $fieldContainer->getModel(); |
44
|
|
|
|
45
|
|
|
if (null === $metadata = $this->getMetadata($model)) { |
46
|
|
|
return; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
$field = $fieldContext->getField(); |
50
|
|
|
|
51
|
|
|
$property = $field->getProperty() ?: $field->getName(); |
52
|
|
|
|
53
|
|
|
if ($metadata->hasAssociation($property)) { |
54
|
|
|
return $this->guessAssociation($metadata, $field, $fieldContext->getSchema()); |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
switch ($metadata->getTypeOfField($property)) { |
58
|
|
|
case DoctrineType::TARRAY: |
59
|
|
|
case DoctrineType::JSON_ARRAY: |
60
|
|
|
return $this->wrapRequired($metadata, $property, '[String]', Guess::LOW_CONFIDENCE); |
61
|
|
|
case DoctrineType::BOOLEAN: |
62
|
|
|
return new TypeGuess('Boolean', Guess::HIGH_CONFIDENCE); |
63
|
|
|
case DoctrineType::DATETIME: |
64
|
|
|
case DoctrineType::DATETIMETZ: |
65
|
|
|
case 'vardatetime': |
66
|
|
|
case DoctrineType::DATE: |
67
|
|
|
case DoctrineType::TIME: |
68
|
|
|
return $this->wrapRequired($metadata, $property, 'String', Guess::MEDIUM_CONFIDENCE); |
69
|
|
|
case DoctrineType::DECIMAL: |
70
|
|
|
case DoctrineType::FLOAT: |
71
|
|
|
return $this->wrapRequired($metadata, $property, 'Number', Guess::MEDIUM_CONFIDENCE); |
72
|
|
|
case DoctrineType::INTEGER: |
73
|
|
|
case DoctrineType::BIGINT: |
74
|
|
|
case DoctrineType::SMALLINT: |
75
|
|
|
return $this->wrapRequired($metadata, $property, 'Int', Guess::MEDIUM_CONFIDENCE); |
76
|
|
|
case DoctrineType::STRING: |
77
|
|
|
return $this->wrapRequired($metadata, $property, 'String', Guess::MEDIUM_CONFIDENCE); |
78
|
|
|
case DoctrineType::TEXT: |
79
|
|
|
return $this->wrapRequired($metadata, $property, 'String', Guess::MEDIUM_CONFIDENCE); |
80
|
|
|
default: |
81
|
|
|
return $this->wrapRequired($metadata, $property, 'String', Guess::LOW_CONFIDENCE); |
82
|
|
|
} |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* {@inheritdoc} |
87
|
|
|
*/ |
88
|
|
View Code Duplication |
public function guessFieldResolveConfig(FieldContext $fieldContext) |
|
|
|
|
89
|
|
|
{ |
90
|
|
|
if (!$this->isFieldContainerSupported($fieldContext)) { |
91
|
|
|
return; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** @var Type $type */ |
95
|
|
|
$type = $fieldContext->getContainer(); |
96
|
|
|
|
97
|
|
|
return new ResolveConfigGuess([ |
98
|
|
|
'handler' => 'doctrine', |
99
|
|
|
'entity' => $type->getModel(), |
100
|
|
|
]); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* {@inheritdoc} |
105
|
|
|
*/ |
106
|
|
View Code Duplication |
public function guessTypeResolveConfig(ContainerContext $containerContext) |
|
|
|
|
107
|
|
|
{ |
108
|
|
|
if (!$this->isFieldContainerSupported($containerContext)) { |
109
|
|
|
return; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** @var Type $type */ |
113
|
|
|
$type = $containerContext->getContainer(); |
114
|
|
|
|
115
|
|
|
return new ResolveConfigGuess([ |
116
|
|
|
'handler' => 'doctrine', |
117
|
|
|
'entity' => $type->getModel(), |
118
|
|
|
]); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param ClassMetadataInfo $metadata |
123
|
|
|
* @param string $property |
124
|
|
|
* @param string $type |
125
|
|
|
* @param int $confidence |
126
|
|
|
* @return TypeGuess |
127
|
|
|
*/ |
128
|
|
|
private function wrapRequired(ClassMetadataInfo $metadata, $property, $type, $confidence) |
129
|
|
|
{ |
130
|
|
|
if (!$metadata->isNullable($property)) { |
131
|
|
|
$type .= '!'; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return new TypeGuess($type, $confidence); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* @param ClassMetadataInfo $metadata |
139
|
|
|
* @param Field $field |
140
|
|
|
* @param SchemaContainer $schemaContainer |
141
|
|
|
* @return TypeGuess |
142
|
|
|
* @throws MappingException |
143
|
|
|
*/ |
144
|
|
|
private function guessAssociation(ClassMetadataInfo $metadata, Field $field, SchemaContainer $schemaContainer) |
145
|
|
|
{ |
146
|
|
|
$property = $field->getProperty() ?: $field->getName(); |
147
|
|
|
$multiple = $metadata->isCollectionValuedAssociation($property); |
148
|
|
|
$mapping = $metadata->getAssociationMapping($property); |
149
|
|
|
|
150
|
|
|
foreach ($schemaContainer->getTypes() as $type) { |
151
|
|
|
$containerContext = new ContainerContext($type, $schemaContainer); |
152
|
|
|
|
153
|
|
|
if (!$this->isFieldContainerSupported($containerContext)) { |
154
|
|
|
continue; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
if ($type->getModel() === $mapping['targetEntity']) { |
158
|
|
|
$typeName = $type->getName(); |
159
|
|
|
if ($multiple) { |
160
|
|
|
$typeName = sprintf('[%s]', $typeName); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
return new TypeGuess($typeName, Guess::HIGH_CONFIDENCE); |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @param ContainerContext $containerContext |
170
|
|
|
* @return bool |
171
|
|
|
*/ |
172
|
|
|
private function isFieldContainerSupported(ContainerContext $containerContext) |
173
|
|
|
{ |
174
|
|
|
$fieldContainer = $containerContext->getContainer(); |
175
|
|
|
if (!$fieldContainer instanceof Type) { |
176
|
|
|
return false; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return !empty($fieldContainer->getModel()); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @param string $class |
184
|
|
|
* @return ClassMetadataInfo |
185
|
|
|
*/ |
186
|
|
|
protected function getMetadata($class) |
187
|
|
|
{ |
188
|
|
|
try { |
189
|
|
|
return $this->metadataFactory->getMetadataFor($class); |
190
|
|
|
} catch (MappingException $e) { |
|
|
|
|
191
|
|
|
// not an entity or mapped super class |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.