EntityHydrator   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Test Coverage

Coverage 87.04%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 78
c 2
b 0
f 0
dl 0
loc 165
ccs 47
cts 54
cp 0.8704
rs 10
wmc 17

3 Methods

Rating   Name   Duplication   Size   Complexity  
B assertHydrationOnEntity() 0 65 8
B hydrateEntity() 0 77 8
A __construct() 0 6 1
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
    }
48
49 3
    public function hydrateEntity($entity, EntityManagerInterface $entityManager): void
50
    {
51
        /** @var array<string> $dataFromAdditionalColumns */
52 3
        $dataFromAdditionalColumns = $this->dbalDataLoader->loadDBALDataForEntity(
53
            $entity,
54
            $entityManager
55
        );
56
57
        /** @var class-string $className */
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 */
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
                    if ($mapping instanceof EntityMappingInterface) {
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
                                "of field '%s' of entity '%s'",
84
                                $fieldName,
85
                                $className
86
                            );
87
88
                            /** @var mixed $value */
89 3
                            $value = $fieldMapping->resolveValue(
90
                                $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
    }
127
128 3
    public function assertHydrationOnEntity($entity, EntityManagerInterface $entityManager): void
129
    {
130
        /** @var array<string> $dataFromAdditionalColumns */
131 3
        $dataFromAdditionalColumns = $this->dbalDataLoader->loadDBALDataForEntity(
132
            $entity,
133
            $entityManager
134
        );
135
136
        /** @var class-string $className */
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 */
148 3
            $mapping = $this->mappingDriver->loadRDMMetadataForClass($className);
149
150 3
            if ($mapping instanceof EntityMappingInterface) {
151 3
                if ($mapping instanceof EntityMappingInterface) {
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
                            $context,
184
                            $dataFromAdditionalColumns,
185
                            $actualValue
186
                        );
187
                    }
188
                }
189
            }
190
191 2
            $className = current(class_parents($className));
192 2
        } while (class_exists($className));
193
    }
194
195
}
196