Passed
Push — master ( 491d8e...9b8ef9 )
by Gerrit
03:23
created

EntityHydrator   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 168
Duplicated Lines 45.24 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 83.33%

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 8
dl 76
loc 168
ccs 55
cts 66
cp 0.8333
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B hydrateEntity() 38 78 8
B assertHydrationOnEntity() 38 66 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\Hydration;
12
13
use ReflectionClass;
14
use ReflectionProperty;
15
use Doctrine\Common\Util\ClassUtils;
16
use Addiks\RDMBundle\Mapping\Drivers\MappingDriverInterface;
17
use Addiks\RDMBundle\Mapping\EntityMappingInterface;
18
use Addiks\RDMBundle\Mapping\MappingInterface;
19
use Addiks\RDMBundle\Hydration\EntityHydratorInterface;
20
use Addiks\RDMBundle\DataLoader\DataLoaderInterface;
21
use Doctrine\ORM\EntityManagerInterface;
22
use Addiks\RDMBundle\Hydration\HydrationContext;
23
use Webmozart\Assert\Assert;
24
use Exception;
25
use Throwable;
26
use Doctrine\ORM\Mapping\MappingException;
27
28
final class EntityHydrator implements EntityHydratorInterface
29
{
30
31
    /**
32
     * @var MappingDriverInterface
33
     */
34
    private $mappingDriver;
35
36
    /**
37
     * @var DataLoaderInterface
38
     */
39
    private $dbalDataLoader;
40
41 5
    public function __construct(
42
        MappingDriverInterface $mappingDriver,
43
        DataLoaderInterface $dbalDataLoader
44
    ) {
45 5
        $this->mappingDriver = $mappingDriver;
46 5
        $this->dbalDataLoader = $dbalDataLoader;
47 5
    }
48
49 3
    public function hydrateEntity($entity, EntityManagerInterface $entityManager): void
50
    {
51
        /** @var array<string> $dataFromAdditionalColumns */
52 3
        $dataFromAdditionalColumns = $this->dbalDataLoader->loadDBALDataForEntity(
53 3
            $entity,
54
            $entityManager
55
        );
56
57
        /** @var class-string $className */
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" 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...
58 3
        $className = get_class($entity);
59
60
        do {
61 3
            if (class_exists(ClassUtils::class)) {
62 3
                $className = ClassUtils::getRealClass($className);
63 3
                Assert::classExists($className);
64
            }
65
66 3
            $classReflection = new ReflectionClass($className);
67
68
            /** @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...
69 3
            $mapping = $this->mappingDriver->loadRDMMetadataForClass($className);
70
71 3
            if ($mapping instanceof EntityMappingInterface) {
72
                /** @var string $processDescription */
73 3
                $processDescription = sprintf("of entity '%s'", $className);
74
75
                try {
76 3 View Code Duplication
                    if ($mapping instanceof EntityMappingInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
77 3
                        $context = new HydrationContext($entity, $entityManager);
78
79 3
                        foreach ($mapping->getFieldMappings() as $fieldName => $fieldMapping) {
80
                            /** @var MappingInterface $fieldMapping */
81
82 3
                            $processDescription = sprintf(
83 3
                                "of field '%s' of entity '%s'",
84
                                $fieldName,
85
                                $className
86
                            );
87
88
                            /** @var mixed $value */
89 3
                            $value = $fieldMapping->resolveValue(
90 3
                                $context,
91
                                $dataFromAdditionalColumns
92
                            );
93
94
                            /** @var ReflectionClass $concreteClassReflection */
95 3
                            $concreteClassReflection = $classReflection;
96
97 3
                            while (!$concreteClassReflection->hasProperty($fieldName)) {
98
                                $concreteClassReflection = $concreteClassReflection->getParentClass();
99
100
                                Assert::object($concreteClassReflection, sprintf(
101
                                    "Property '%s' does not exist on object of class '%s'!",
102
                                    $fieldName,
103
                                    $className
104
                                ));
105
                            }
106
107
                            /** @var ReflectionProperty $propertyReflection */
108 3
                            $propertyReflection = $concreteClassReflection->getProperty($fieldName);
109
110 3
                            $propertyReflection->setAccessible(true);
111 3
                            $propertyReflection->setValue($entity, $value);
112
                        }
113
                    }
114
115
                } catch (Throwable $exception) {
116
                    throw new MappingException(sprintf(
117
                        "Exception during hydration %s: '%s'!",
118
                        $processDescription,
119
                        $exception->getMessage()
120
                    ), 0, $exception);
121
                }
122
            }
123
124 3
            $className = current(class_parents($className));
125 3
        } while (class_exists($className));
126 3
    }
127
128 3
    public function assertHydrationOnEntity($entity, EntityManagerInterface $entityManager): void
129
    {
130
        /** @var array<string> $dataFromAdditionalColumns */
131 3
        $dataFromAdditionalColumns = $this->dbalDataLoader->loadDBALDataForEntity(
132 3
            $entity,
133
            $entityManager
134
        );
135
136
        /** @var class-string $className */
0 ignored issues
show
Documentation introduced by
The doc-type class-string could not be parsed: Unknown type name "class-string" 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...
137 3
        $className = get_class($entity);
138
139
        do {
140 3
            if (class_exists(ClassUtils::class)) {
141 3
                $className = ClassUtils::getRealClass($className);
142 3
                Assert::classExists($className);
143
            }
144
145 3
            $classReflection = new ReflectionClass($className);
146
147
            /** @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...
148 3
            $mapping = $this->mappingDriver->loadRDMMetadataForClass($className);
149
150 3
            if ($mapping instanceof EntityMappingInterface) {
151 3 View Code Duplication
                if ($mapping instanceof EntityMappingInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
152 3
                    $context = new HydrationContext($entity, $entityManager);
153
154 3
                    foreach ($mapping->getFieldMappings() as $fieldName => $fieldMapping) {
155
                        /** @var MappingInterface $fieldMapping */
156
157
                        /** @var ReflectionClass $concreteClassReflection */
158 3
                        $concreteClassReflection = $classReflection;
159
160 3
                        while (!$concreteClassReflection->hasProperty($fieldName)) {
161
                            $concreteClassReflection = $concreteClassReflection->getParentClass();
162
163
                            Assert::notFalse($concreteClassReflection, sprintf(
164
                                "Property '%s' does not exist on object of class '%s'!",
165
                                $fieldName,
166
                                $className
167
                            ));
168
                        }
169
170
                        /** @var ReflectionProperty $propertyReflection */
171 3
                        $propertyReflection = $concreteClassReflection->getProperty($fieldName);
172
173 3
                        $propertyReflection->setAccessible(true);
174
175
                        /** @var object|null $actualValue */
176 3
                        $actualValue = null;
177
178 3
                        if ($propertyReflection->isInitialized($entity)) {
179 3
                            $actualValue = $propertyReflection->getValue($entity);
180
                        }
181
182 3
                        $fieldMapping->assertValue(
183 3
                            $context,
184
                            $dataFromAdditionalColumns,
185
                            $actualValue
186
                        );
187
                    }
188
                }
189
            }
190
191 2
            $className = current(class_parents($className));
192 2
        } while (class_exists($className));
193 2
    }
194
195
}
196