Completed
Push — master ( 77cc29...840912 )
by Gerrit
10:19
created

MappingAnnotationDriver   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 96.58%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 15
dl 0
loc 230
ccs 113
cts 117
cp 0.9658
rs 9.1666
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B loadRDMMetadataForClass() 0 36 5
D convertAnnotationToMapping() 0 167 13
1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 * @license GPL-3.0
8
 * @author Gerrit Addiks <[email protected]>
9
 */
10
11
namespace Addiks\RDMBundle\Mapping\Drivers;
12
13
use Doctrine\Common\Annotations\Reader;
14
use ReflectionClass;
15
use ReflectionProperty;
16
use Addiks\RDMBundle\Mapping\Drivers\MappingDriverInterface;
17
use Addiks\RDMBundle\Mapping\MappingInterface;
18
use Addiks\RDMBundle\Mapping\EntityMapping;
19
use Addiks\RDMBundle\Mapping\Annotation\Service;
20
use Addiks\RDMBundle\Mapping\ServiceMapping;
21
use Addiks\RDMBundle\Mapping\EntityMappingInterface;
22
use Addiks\RDMBundle\Mapping\Annotation\Choice;
23
use Addiks\RDMBundle\Mapping\ChoiceMapping;
24
use Addiks\RDMBundle\Exception\InvalidMappingException;
25
use Doctrine\ORM\Mapping\Column as ColumnAnnotation;
26
use Doctrine\DBAL\Schema\Column as DBALColumn;
27
use Doctrine\DBAL\Types\Type;
28
use Addiks\RDMBundle\Mapping\ObjectMapping;
29
use Addiks\RDMBundle\Mapping\CallDefinitionInterface;
30
use Addiks\RDMBundle\Mapping\FieldMapping;
31
use Addiks\RDMBundle\Mapping\ArrayMapping;
32
use Addiks\RDMBundle\Mapping\Annotation\RDMObject;
33
use Addiks\RDMBundle\Mapping\Annotation\RDMArray;
34
use Symfony\Component\DependencyInjection\ContainerInterface;
35
36
final class MappingAnnotationDriver implements MappingDriverInterface
37
{
38
39
    /**
40
     * @var ContainerInterface
41
     */
42
    private $container;
43
44
    /**
45
     * @var Reader
46
     */
47
    private $annotationReader;
48
49 7
    public function __construct(
50
        ContainerInterface $container,
51
        Reader $annotationReader
52
    ) {
53 7
        $this->container = $container;
54 7
        $this->annotationReader = $annotationReader;
55 7
    }
56
57 6
    public function loadRDMMetadataForClass(string $className): ?EntityMappingInterface
58
    {
59
        /** @var ?EntityMappingInterface $mapping */
0 ignored issues
show
Documentation introduced by
The doc-type ?EntityMappingInterface could not be parsed: Unknown type name "?EntityMappingInterface" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
60 6
        $mapping = null;
61
62
        /** @var array<MappingInterface> $fieldMappings */
63 6
        $fieldMappings = array();
64
65 6
        $classReflection = new ReflectionClass($className);
66
67 6
        foreach ($classReflection->getProperties() as $propertyReflection) {
68
            /** @var ReflectionProperty $propertyReflection */
69
70
            /** @var string $fieldName */
71 6
            $fieldName = $propertyReflection->getName();
72
73
            /** @var array<object> $annotations */
74 6
            $annotations = $this->annotationReader->getPropertyAnnotations($propertyReflection);
75
76 6
            foreach ($annotations as $annotation) {
77
                /** @var object $annotation */
78
79 6
                $fieldMapping = $this->convertAnnotationToMapping($annotation, $fieldName, $className);
80
81 6
                if ($fieldMapping instanceof MappingInterface) {
82 6
                    $fieldMappings[$fieldName] = $fieldMapping;
83
                }
84
            }
85
        }
86
87 6
        if (!empty($fieldMappings)) {
88 6
            $mapping = new EntityMapping($className, $fieldMappings);
89
        }
90
91 6
        return $mapping;
92
    }
93
94
    /**
95
     * @param object $annotation
96
     */
97 6
    private function convertAnnotationToMapping(
98
        $annotation,
99
        string $fieldName,
100
        string $className,
101
        bool $convertFieldsOnRootLevel = false
102
    ): ?MappingInterface {
103
        /** @var ?MappingInterface $fieldMapping */
0 ignored issues
show
Documentation introduced by
The doc-type ?MappingInterface could not be parsed: Unknown type name "?MappingInterface" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
104 6
        $fieldMapping = null;
105
106 6
        if ($annotation instanceof Service) {
107 5
            $fieldMapping = new ServiceMapping(
108 5
                $this->container,
109 5
                $annotation->id,
110 5
                $annotation->lax,
111 5
                sprintf(
112 5
                    "in entity '%s' on field '%s'",
113 5
                    $className,
114 5
                    $fieldName
115
                )
116
            );
117
118 6
        } elseif ($annotation instanceof Choice) {
119
            /** @var string|null|ColumnAnnotation $column */
120 4
            $column = $annotation->column;
121
122 4
            if (!$column instanceof ColumnAnnotation) {
123
                /** @var string $columnName */
124 4
                $columnName = $fieldName;
125
126 4
                if (is_string($column)) {
127 4
                    $columnName = $column;
128
                }
129
130 4
                $column = new ColumnAnnotation();
131 4
                $column->name = $columnName;
132 4
                $column->type = 'string';
133 4
                $column->length = 255;
134 4
                $column->precision = 10;
135 4
                $column->nullable = (bool)$annotation->nullable;
136
            }
137
138 4
            $dbalColumn = new DBALColumn(
139 4
                $column->name,
140 4
                Type::getType($column->type),
141
                [
142 4
                    'notnull'   => !$column->nullable,
143 4
                    'length'    => $column->length,
144 4
                    'precision' => $column->precision,
145 4
                    'scale'     => $column->scale,
146
                ]
147
            );
148
149
            /** @var array<MappingInterface>*/
150 4
            $choiceMappings = array();
151
152 4
            foreach ($annotation->choices as $determinator => $choiceAnnotation) {
153
                /** @var ?MappingInterface $choiceMapping */
0 ignored issues
show
Documentation introduced by
The doc-type ?MappingInterface could not be parsed: Unknown type name "?MappingInterface" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
154 3
                $choiceMapping = $this->convertAnnotationToMapping(
155 3
                    $choiceAnnotation,
156 3
                    $fieldName,
157 3
                    $className
158
                );
159
160 3
                if ($choiceMapping instanceof MappingInterface) {
161 3
                    $choiceMappings[$determinator] = $choiceMapping;
162
163
                } else {
164
                    throw new InvalidMappingException(sprintf(
165
                        "Invalid mapping on entity '%s' in field '%s' of choice-option '%s'!",
166
                        $className,
167
                        $fieldName,
168 3
                        $determinator
169
                    ));
170
                }
171
            }
172
173 4
            $fieldMapping = new ChoiceMapping(
174 4
                $dbalColumn,
175 4
                $choiceMappings,
176 4
                sprintf(
177 4
                    "in entity '%s' on field '%s'",
178 4
                    $className,
179 4
                    $fieldName
180
                )
181
            );
182
183 6
        } elseif ($annotation instanceof RDMObject) {
184
            /** @var array<MappingInterface> $subFieldMappings */
185 2
            $subFieldMappings = array();
186
187
            /** @var null|CallDefinitionInterface $factory */
188 2
            $factory = null;
189
190
            /** @var null|CallDefinitionInterface $serializer */
191 2
            $serializer = null;
192
193 2
            foreach ($annotation->fields as $subFieldName => $subFieldAnnotation) {
194
                /** @var object $fieldAnnotation */
195
196 2
                $subFieldMappings[$subFieldName] = $this->convertAnnotationToMapping(
197 2
                    $subFieldAnnotation,
198 2
                    $fieldName . "->" . $subFieldName,
199 2
                    $className,
200 2
                    true
201
                );
202
            }
203
204 2
            $fieldMapping = new ObjectMapping(
205 2
                $annotation->{"class"},
206 2
                $subFieldMappings,
207 2
                null, # TODO: column
208 2
                sprintf(
209 2
                    "in entity '%s' on field '%s'",
210 2
                    $className,
211 2
                    $fieldName
212
                ),
213 2
                $factory,
214 2
                $serializer
215
            );
216
217 6
        } elseif ($annotation instanceof RDMArray) {
218
            /** @var array<MappingInterface> $entryMappings */
219 2
            $entryMappings = array();
220
221 2
            foreach ($annotation->entries as $key => $entryAnnotaton) {
222 2
                $entryMappings[$key] = $this->convertAnnotationToMapping(
223 2
                    $entryAnnotaton,
224 2
                    $fieldName . "->" . $key,
225 2
                    $className,
226 2
                    true
227
                );
228
            }
229
230 2
            $fieldMapping = new ArrayMapping(
231 2
                $entryMappings,
232 2
                sprintf(
233 2
                    "in entity '%s' on field '%s'",
234 2
                    $className,
235 2
                    $fieldName
236
                )
237
            );
238
239 6
        } elseif ($annotation instanceof ColumnAnnotation && $convertFieldsOnRootLevel) {
240
            /** @var DBALColumn $dbalColumn */
241 2
            $dbalColumn = new DBALColumn(
242 2
                $annotation->name,
243 2
                Type::getType($annotation->type),
244
                [
245 2
                    'notnull'   => !$annotation->nullable,
246 2
                    'length'    => $annotation->length,
247 2
                    'precision' => $annotation->precision,
248 2
                    'scale'     => $annotation->scale,
249
                ]
250
            );
251
252 2
            $fieldMapping = new FieldMapping(
253 2
                $dbalColumn,
254 2
                sprintf(
255 2
                    "in entity '%s' on field '%s'",
256 2
                    $className,
257 2
                    $fieldName
258
                )
259
            );
260
        }
261
262 6
        return $fieldMapping;
263
    }
264
265
}
266