Completed
Push — master ( 1f339d...13b514 )
by Gerrit
04:45
created

ObjectMapping::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 13
cts 13
cp 1
rs 9.488
c 0
b 0
f 0
cc 2
nc 2
nop 8
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
 *
8
 * @license GPL-3.0
9
 *
10
 * @author Gerrit Addiks <[email protected]>
11
 */
12
13
namespace Addiks\RDMBundle\Mapping;
14
15
use Doctrine\DBAL\Schema\Column;
16
use Addiks\RDMBundle\Mapping\MappingInterface;
17
use Webmozart\Assert\Assert;
18
use Addiks\RDMBundle\Hydration\HydrationContextInterface;
19
use Addiks\RDMBundle\Mapping\CallDefinitionInterface;
20
use Addiks\RDMBundle\Exception\FailedRDMAssertionException;
21
use ReflectionClass;
22
use ReflectionProperty;
23
use ReflectionException;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use Doctrine\DBAL\Types\Type;
26
use Doctrine\DBAL\Connection;
27
28
final class ObjectMapping implements MappingInterface
29
{
30
31
    /**
32
     * @var string
33
     */
34
    private $className;
35
36
    /**
37
     * @var array<MappingInterface>
38
     */
39
    private $fieldMappings = array();
40
41
    /**
42
     * @var Column|null
43
     */
44
    private $column;
45
46
    /**
47
     * @var CallDefinitionInterface|null
48
     */
49
    private $factory;
50
51
    /**
52
     * @var CallDefinitionInterface|null
53
     */
54
    private $serializer;
55
56
    /**
57
     * @var string
58
     */
59
    private $origin;
60
61
    /**
62
     * @var string|null
63
     */
64
    private $id;
65
66
    /**
67
     * @var string|null
68
     */
69
    private $referencedId;
70
71 17
    public function __construct(
72
        string $className,
73
        array $fieldMappings,
74
        Column $column = null,
75
        string $origin = "undefined",
76
        CallDefinitionInterface $factory = null,
77
        CallDefinitionInterface $serializer = null,
78
        string $id = null,
79
        string $referencedId = null
80
    ) {
81 17
        $this->className = $className;
82 17
        $this->factory = $factory;
83 17
        $this->column = $column;
84 17
        $this->serializer = $serializer;
85 17
        $this->origin = $origin;
86 17
        $this->id = $id;
87 17
        $this->referencedId = $referencedId;
88
89 17
        foreach ($fieldMappings as $fieldName => $fieldMapping) {
90
            /** @var MappingInterface $fieldMapping */
91
92 17
            Assert::isInstanceOf($fieldMapping, MappingInterface::class);
93 17
            Assert::false(is_numeric($fieldName));
94
95 17
            $this->fieldMappings[$fieldName] = $fieldMapping;
96
        }
97 17
    }
98
99 1
    public function getClassName(): string
100
    {
101 1
        return $this->className;
102
    }
103
104 1
    public function getDBALColumn(): ?Column
105
    {
106 1
        return $this->column;
107
    }
108
109 1
    public function getFieldMappings(): array
110
    {
111 1
        return $this->fieldMappings;
112
    }
113
114 1
    public function describeOrigin(): string
115
    {
116 1
        return $this->origin;
117
    }
118
119 2
    public function collectDBALColumns(): array
120
    {
121
        /** @var array<Column> $additionalColumns */
122 2
        $additionalColumns = array();
123
124 2
        if ($this->column instanceof Column) {
125 1
            $additionalColumns[] = $this->column;
126
        }
127
128 2
        foreach ($this->fieldMappings as $fieldMapping) {
129
            /** @var MappingInterface $fieldMapping */
130
131 2
            $additionalColumns = array_merge(
132 2
                $additionalColumns,
133 2
                $fieldMapping->collectDBALColumns()
134
            );
135
        }
136
137 2
        return $additionalColumns;
138
    }
139
140 1
    public function getFactory(): ?CallDefinitionInterface
141
    {
142 1
        return $this->factory;
143
    }
144
145 1
    public function getSerializer(): ?CallDefinitionInterface
146
    {
147 1
        return $this->serializer;
148
    }
149
150 1
    public function getId(): ?string
151
    {
152 1
        return $this->id;
153
    }
154
155 1
    public function getReferencedId(): ?string
156
    {
157 1
        return $this->referencedId;
158
    }
159
160 3
    public function resolveValue(
161
        HydrationContextInterface $context,
162
        array $dataFromAdditionalColumns
163
    ) {
164
        /** @var mixed $object */
165 3
        $object = null;
0 ignored issues
show
Unused Code introduced by
$object is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
166
167 3
        $reflectionClass = new ReflectionClass($this->className);
168
169
        /** @var object|string $object */
170 3
        $object = $this->className;
171
172 3
        if ($reflectionClass->isInstantiable()) {
173 3
            $object = $reflectionClass->newInstanceWithoutConstructor();
174
        }
175
176 3
        $context->pushOnObjectHydrationStack($object);
177
178 3
        if (!empty($this->referencedId)) {
179
            $object = $context->getRegisteredValue($this->referencedId);
180
181 3
        } elseif ($this->factory instanceof CallDefinitionInterface) {
182
            /** @var array<string, string> $factoryData */
183 2
            $factoryData = $dataFromAdditionalColumns;
184
185 2
            if ($this->column instanceof Column && !array_key_exists("", $factoryData)) {
186
                /** @var Type $type */
187 1
                $type = $this->column->getType();
188
189
                /** @var string $columnName */
190 1
                $columnName = $this->column->getName();
191
192 1
                if (isset($dataFromAdditionalColumns[$columnName])) {
193
                    /** @var Connection $connection */
194
                    $connection = $context->getEntityManager()->getConnection();
195
196
                    $dataFromAdditionalColumns[$columnName] = $type->convertToPHPValue(
197
                        $dataFromAdditionalColumns[$columnName],
198
                        $connection->getDatabasePlatform()
199
                    );
200
201
                    $factoryData[""] = $dataFromAdditionalColumns[$columnName];
202
                }
203
            }
204
205 2
            $object = $this->factory->execute(
206 2
                $context,
207 2
                $factoryData
208
            );
209
        }
210
211
        // $object may have been replaced during creation, re-assign on top of stack.
212 3
        $context->popFromObjectHydrationStack();
213 3
        $context->pushOnObjectHydrationStack($object);
214
215 3
        if (!empty($this->id) && !$context->hasRegisteredValue($this->id)) {
216 2
            $context->registerValue($this->id, $object);
217
        }
218
219 3
        foreach ($this->fieldMappings as $fieldName => $fieldMapping) {
220
            /** @var MappingInterface $fieldMapping */
221
222
            /** @var mixed $fieldValue */
223 3
            $fieldValue = $fieldMapping->resolveValue(
224 3
                $context,
225 3
                $dataFromAdditionalColumns
226
            );
227
228
            /** @var ReflectionClass $propertyReflectionClass */
229 3
            $propertyReflectionClass = $reflectionClass;
230
231 3
            while (is_object($propertyReflectionClass) && !$propertyReflectionClass->hasProperty($fieldName)) {
232
                $propertyReflectionClass = $propertyReflectionClass->getParentClass();
233
            }
234
235 3
            if (!is_object($propertyReflectionClass)) {
236
                throw new ReflectionException(sprintf("Property %s does not exist", $fieldName));
237
            }
238
239
            /** @var ReflectionProperty $reflectionProperty */
240 3
            $reflectionProperty = $propertyReflectionClass->getProperty($fieldName);
241
242 3
            $reflectionProperty->setAccessible(true);
243 3
            $reflectionProperty->setValue($object, $fieldValue);
244
        }
245
246 3
        $context->popFromObjectHydrationStack();
247
248 3
        return $object;
249
    }
250
251 2
    public function revertValue(
252
        HydrationContextInterface $context,
253
        $valueFromEntityField
254
    ): array {
255
        /** @var array<scalar> $data */
256 2
        $data = array();
257
258 2
        $this->assertValue($context, [], $valueFromEntityField);
259
260 2
        $reflectionClass = new ReflectionClass($this->className);
261
262 2
        $context->pushOnObjectHydrationStack($valueFromEntityField);
263
264 2
        foreach ($this->fieldMappings as $fieldName => $fieldMapping) {
265
            /** @var MappingInterface $fieldMapping */
266
267
            /** @var ReflectionClass $propertyReflectionClass */
268 2
            $propertyReflectionClass = $reflectionClass;
269
270 2
            while (is_object($propertyReflectionClass) && !$propertyReflectionClass->hasProperty($fieldName)) {
271
                $propertyReflectionClass = $propertyReflectionClass->getParentClass();
272
            }
273
274 2
            if (!is_object($propertyReflectionClass)) {
275
                throw new ReflectionException(sprintf(
276
                    "Property %s does not exist in class %s. (Defined %s)",
277
                    $fieldName,
278
                    get_class($valueFromEntityField),
279
                    $this->origin
280
                ));
281
            }
282
283
            /** @var ReflectionProperty $reflectionProperty */
284 2
            $reflectionProperty = $propertyReflectionClass->getProperty($fieldName);
285
286 2
            $reflectionProperty->setAccessible(true);
287 2
            $valueFromField = null;
288
289 2
            if (is_object($valueFromEntityField)) {
290 1
                $valueFromField = $reflectionProperty->getValue($valueFromEntityField);
291
            }
292
293
            /** @var array<string, mixed> $fieldData */
294 2
            $fieldData = $fieldMapping->revertValue(
295 2
                $context,
296 2
                $valueFromField
297
            );
298
299 2
            if (array_key_exists("", $fieldData)) {
300
                $fieldData[$fieldName] = $fieldData[""];
301
                unset($fieldData[""]);
302
            }
303
304 2
            $data = array_merge($data, $fieldData);
305
        }
306
307 2
        if ($this->serializer instanceof CallDefinitionInterface) {
308
            /** @var string $columnName */
309
            $columnName = '';
310
311
            if ($this->column instanceof Column) {
312
                $columnName = $this->column->getName();
313
            }
314
315
            $data[$columnName] = $this->serializer->execute(
316
                $context,
317
                array_merge($data, ['' => $valueFromEntityField])
318
            );
319
320
            if ($this->column instanceof Column) {
321
                /** @var Type $type */
322
                $type = $this->column->getType();
323
324
                /** @var Connection $connection */
325
                $connection = $context->getEntityManager()->getConnection();
326
327
                $data[$columnName] = $type->convertToDatabaseValue(
328
                    $data[$columnName],
329
                    $connection->getDatabasePlatform()
330
                );
331
            }
332
        }
333
334 2
        $context->popFromObjectHydrationStack();
335
336 2
        return $data;
337
    }
338
339 3
    public function assertValue(
340
        HydrationContextInterface $context,
341
        array $dataFromAdditionalColumns,
342
        $actualValue
343
    ): void {
344 3
        if (!is_null($actualValue) && !$actualValue instanceof $this->className) {
345 1
            throw FailedRDMAssertionException::expectedInstanceOf(
346 1
                $this->className,
347 1
                is_object($actualValue) ?get_class($actualValue) :gettype($actualValue),
348 1
                $this->origin
349
            );
350
        }
351 2
    }
352
353 1
    public function wakeUpMapping(ContainerInterface $container): void
354
    {
355 1
        if ($this->factory instanceof CallDefinitionInterface) {
356 1
            $this->factory->wakeUpCall($container);
357
        }
358
359 1
        if ($this->serializer instanceof CallDefinitionInterface) {
360 1
            $this->serializer->wakeUpCall($container);
361
        }
362 1
    }
363
364
}
365