Completed
Branch master (895aee)
by Arthur
04:23 queued 02:09
created

DoctrineGuesser::guessAssociation()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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