Passed
Push — master ( 356755...14835f )
by Gerrit
04:25
created

ObjectValueResolver   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 233
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 77.08%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 9
dl 0
loc 233
ccs 74
cts 96
cp 0.7708
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
C resolveValue() 0 99 11
C revertValue() 0 83 8
A assertValue() 0 19 4
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\ValueResolver;
14
15
use ReflectionClass;
16
use Symfony\Component\DependencyInjection\ContainerInterface;
17
use Addiks\RDMBundle\ValueResolver\ValueResolverInterface;
18
use Addiks\RDMBundle\Mapping\ObjectMappingInterface;
19
use Addiks\RDMBundle\Mapping\CallDefinitionInterface;
20
use Addiks\RDMBundle\Mapping\MappingInterface;
21
use ReflectionProperty;
22
use Addiks\RDMBundle\Exception\FailedRDMAssertionException;
23
use Addiks\RDMBundle\ValueResolver\CallDefinitionExecuterInterface;
24
use Doctrine\DBAL\Schema\Column;
25
use Addiks\RDMBundle\Hydration\HydrationContextInterface;
26
use Doctrine\DBAL\Types\Type;
27
use Doctrine\DBAL\Connection;
28
29
final class ObjectValueResolver implements ValueResolverInterface
30
{
31
32
    /**
33
     * @var ContainerInterface
34
     */
35
    private $container;
36
37
    /**
38
     * @var ValueResolverInterface
39
     */
40
    private $fieldValueResolver;
41
42
    /**
43
     * @var CallDefinitionExecuterInterface
44
     */
45
    private $callDefinitionExecuter;
46
47 8
    public function __construct(
48
        ContainerInterface $container,
49
        ValueResolverInterface $fieldValueResolver,
50
        CallDefinitionExecuterInterface $callDefinitionExecuter
51
    ) {
52 8
        $this->container = $container;
53 8
        $this->fieldValueResolver = $fieldValueResolver;
54 8
        $this->callDefinitionExecuter = $callDefinitionExecuter;
55 8
    }
56
57 3
    public function resolveValue(
58
        MappingInterface $objectMapping,
59
        HydrationContextInterface $context,
60
        array $dataFromAdditionalColumns
61
    ) {
62
        /** @var mixed $object */
63 3
        $object = null;
64
65 3
        if ($objectMapping instanceof ObjectMappingInterface) {
66
            /** @var string $className */
67 3
            $className = $objectMapping->getClassName();
68
69
            /** @var null|CallDefinitionInterface $factory */
70 3
            $factory = $objectMapping->getFactory();
71
72
            /** @var string|null $id */
73 3
            $id = $objectMapping->getId();
74
75
            /** @var string|null $referencedId */
76 3
            $referencedId = $objectMapping->getReferencedId();
77
78 3
            $reflectionClass = new ReflectionClass($className);
79
80
            /** @var object|string $object */
81 3
            $object = $className;
82
83 3
            if ($reflectionClass->isInstantiable()) {
84 3
                $object = $reflectionClass->newInstanceWithoutConstructor();
85
            }
86
87 3
            $context->pushOnObjectHydrationStack($object);
88
89 3
            if (!empty($referencedId)) {
90
                $object = $context->getRegisteredValue($referencedId);
91
92 3
            } elseif ($factory instanceof CallDefinitionInterface) {
93
                /** @var array<string, string> $factoryData */
94 2
                $factoryData = $dataFromAdditionalColumns;
95
96
                /** @var Column|null $column */
97 2
                $column = $objectMapping->getDBALColumn();
98
99 2
                if ($column instanceof Column && !array_key_exists("", $factoryData)) {
100
                    /** @var Type $type */
101 1
                    $type = $column->getType();
102
103
                    /** @var string $columnName */
104 1
                    $columnName = $column->getName();
105
106 1
                    if (isset($dataFromAdditionalColumns[$columnName])) {
107
                        /** @var Connection $connection */
108
                        $connection = $context->getEntityManager()->getConnection();
109
110
                        $dataFromAdditionalColumns[$columnName] = $type->convertToPHPValue(
111
                            $dataFromAdditionalColumns[$columnName],
112
                            $connection->getDatabasePlatform()
113
                        );
114
115
                        $factoryData[""] = $dataFromAdditionalColumns[$columnName];
116
                    }
117
                }
118
119 2
                $object = $this->callDefinitionExecuter->executeCallDefinition(
120 2
                    $factory,
121 2
                    $context,
122 2
                    $factoryData
123
                );
124
            }
125
126
            // $object may have been replaced during creation, re-assign on top of stack.
127 3
            $context->popFromObjectHydrationStack();
128 3
            $context->pushOnObjectHydrationStack($object);
129
130 3
            if (!empty($id) && !$context->hasRegisteredValue($id)) {
131 2
                $context->registerValue($id, $object);
132
            }
133
134 3
            foreach ($objectMapping->getFieldMappings() as $fieldName => $fieldMapping) {
135
                /** @var MappingInterface $fieldMapping */
136
137
                /** @var mixed $fieldValue */
138 3
                $fieldValue = $this->fieldValueResolver->resolveValue(
139 3
                    $fieldMapping,
140 3
                    $context,
141 3
                    $dataFromAdditionalColumns
142
                );
143
144
                /** @var ReflectionProperty $reflectionProperty */
145 3
                $reflectionProperty = $reflectionClass->getProperty($fieldName);
146
147 3
                $reflectionProperty->setAccessible(true);
148 3
                $reflectionProperty->setValue($object, $fieldValue);
149
            }
150
151 3
            $context->popFromObjectHydrationStack();
152
        }
153
154 3
        return $object;
155
    }
156
157 2
    public function revertValue(
158
        MappingInterface $objectMapping,
159
        HydrationContextInterface $context,
160
        $valueFromEntityField
161
    ): array {
162
        /** @var array<scalar> $data */
163 2
        $data = array();
164
165 2
        if ($objectMapping instanceof ObjectMappingInterface) {
166
            /** @var string $className */
167 2
            $className = $objectMapping->getClassName();
168
169 2
            $reflectionClass = new ReflectionClass($className);
170
171 2
            $context->pushOnObjectHydrationStack($valueFromEntityField);
172
173 2
            foreach ($objectMapping->getFieldMappings() as $fieldName => $fieldMapping) {
174
                /** @var MappingInterface $fieldMapping */
175
176
                /** @var ReflectionProperty $reflectionProperty */
177 2
                $reflectionProperty = $reflectionClass->getProperty($fieldName);
178
179 2
                $reflectionProperty->setAccessible(true);
180 2
                $valueFromField = null;
181
182 2
                if (is_object($valueFromEntityField)) {
183 1
                    $valueFromField = $reflectionProperty->getValue($valueFromEntityField);
184
                }
185
186
                /** @var array<string, mixed> $fieldData */
187 2
                $fieldData = $this->fieldValueResolver->revertValue(
188 2
                    $fieldMapping,
189 2
                    $context,
190 2
                    $valueFromField
191
                );
192
193 2
                if (array_key_exists("", $fieldData)) {
194
                    $fieldData[$fieldName] = $fieldData[""];
195
                    unset($fieldData[""]);
196
                }
197
198 2
                $data = array_merge($data, $fieldData);
199
            }
200
201
            /** @var null|CallDefinitionInterface $serializerMapping */
202 2
            $serializerMapping = $objectMapping->getSerializer();
203
204 2
            if ($serializerMapping instanceof CallDefinitionInterface) {
205
                /** @var string $columnName */
206
                $columnName = '';
207
208
                /** @var Column|null $column */
209
                $column = $objectMapping->getDBALColumn();
210
211
                if ($column instanceof Column) {
212
                    $columnName = $column->getName();
213
                }
214
215
                $data[$columnName] = $this->callDefinitionExecuter->executeCallDefinition(
216
                    $serializerMapping,
217
                    $context,
218
                    $data
219
                );
220
221
                if ($column instanceof Column) {
222
                    /** @var Type $type */
223
                    $type = $column->getType();
224
225
                    /** @var Connection $connection */
226
                    $connection = $context->getEntityManager()->getConnection();
227
228
                    $data[$columnName] = $type->convertToDatabaseValue(
229
                        $data[$columnName],
230
                        $connection->getDatabasePlatform()
231
                    );
232
                }
233
            }
234
235 2
            $context->popFromObjectHydrationStack();
236
        }
237
238 2
        return $data;
239
    }
240
241 2
    public function assertValue(
242
        MappingInterface $objectMapping,
243
        HydrationContextInterface $context,
244
        array $dataFromAdditionalColumns,
245
        $actualValue
246
    ): void {
247 2
        if ($objectMapping instanceof ObjectMappingInterface) {
248
            /** @var string $className */
249 2
            $className = $objectMapping->getClassName();
250
251 2
            if (!is_null($actualValue) && !$actualValue instanceof $className) {
252 1
                throw FailedRDMAssertionException::expectedInstanceOf(
253 1
                    $objectMapping->getClassName(),
254 1
                    $context->getEntityClass(),
255 1
                    $objectMapping->describeOrigin()
256
                );
257
            }
258
        }
259 1
    }
260
261
}
262