Passed
Pull Request — 2.x (#432)
by Aleksei
20:22
created

ClosureHydrator::setRelationProperties()   C

Complexity

Conditions 12
Paths 3

Size

Total Lines 52
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 12

Importance

Changes 0
Metric Value
cc 12
eloc 28
nc 3
nop 4
dl 0
loc 52
ccs 12
cts 12
cp 1
crap 12
rs 6.9666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\ORM\Mapper\Proxy\Hydrator;
6
7
use Closure;
8
use Cycle\ORM\EntityProxyInterface;
9
use Cycle\ORM\Exception\MapperException;
10
use Cycle\ORM\Reference\ReferenceInterface;
11
use Cycle\ORM\RelationMap;
12
13
/**
14
 * @internal
15
 */
16
class ClosureHydrator
17
{
18
    /**
19
     * @param array<string, PropertyMap> $propertyMaps Array of class properties
20 4804
     */
21
    public function hydrate(RelationMap $relMap, array $propertyMaps, object $object, array $data): object
22 4804
    {
23
        $isProxy = $object instanceof EntityProxyInterface;
24 4804
25 4804
        $properties = $propertyMaps[ClassPropertiesExtractor::KEY_FIELDS]->getProperties();
26
        $this->setEntityProperties($properties, $object, $data);
27 4804
28 1458
        if (!$isProxy) {
29 1458
            $properties = $propertyMaps[ClassPropertiesExtractor::KEY_RELATIONS]->getProperties();
30 928
            if ($properties !== []) {
31
                $this->setRelationProperties($properties, $relMap, $object, $data);
32
            }
33
        }
34 4804
35 4702
        foreach ($data as $property => $value) {
36
            try {
37
                if (isset($relMap->getRelations()[$property])) {
38 4804
                    unset($object->{$property});
39
                }
40
                // Use @ to try to ignore deprecations
41
                @$object->{$property} = $value;
42
            } catch (\Throwable $e) {
43
                if ($e::class === \TypeError::class) {
44 4804
                    throw new MapperException(
45
                        "Can't hydrate an entity because property and value types are incompatible.",
46 4804
                        previous: $e
47 4804
                    );
48 4790
                }
49
            }
50
        }
51 3340
52 3340
        return $object;
53 3340
    }
54 3208
55
    /**
56
     * Map private entity properties
57 244
     */
58 244
    private function setEntityProperties(array $properties, object $object, array &$data): void
59
    {
60
        foreach ($properties as $class => $props) {
61
            if ($class === '') {
62
                continue;
63
            }
64
65
            Closure::bind(static function (object $object, array $props, array &$data): void {
66
                foreach ($props as $property) {
67 928
                    if (!array_key_exists($property, $data)) {
68
                        continue;
69 928
                    }
70
71 928
                    try {
72 928
                        // Use @ to try to ignore deprecations
73 866
                        @$object->{$property} = $data[$property];
74
                        unset($data[$property]);
75
                    } catch (\Throwable $e) {
76 64
                        if ($e::class === \TypeError::class) {
77 64
                            throw new MapperException(
78 64
                                "Can't hydrate an entity because property and value types are incompatible.",
79 62
                                previous: $e
80
                            );
81
                        }
82 16
                    }
83
                }
84 16
            }, null, $class)($object, $props, $data);
85 8
        }
86
    }
87 8
88
    /**
89 8
     * Map private relations of non-proxy entity
90
     */
91 8
    private function setRelationProperties(array $properties, RelationMap $relMap, object $object, array &$data): void
92
    {
93 8
        $refl = new \ReflectionClass($object);
94 8
        $setter = static function (object $object, array $props, array &$data) use ($refl, $relMap): void {
95 8
            foreach ($props as $property) {
96 2
                if (!\array_key_exists($property, $data)) {
97 2
                    continue;
98
                }
99
100 2
                $value = $data[$property];
101
102
                if ($value instanceof ReferenceInterface) {
103
                    $prop = $refl->getProperty($property);
104 6
105 6
                    if ($prop->hasType()) {
106 6
                        // todo: we can cache this
107
                        /** @var \ReflectionNamedType[] $types */
108
                        $types = $prop->getType() instanceof \ReflectionUnionType
109
                            ? $prop->getType()->getTypes()
110
                            : [$prop->getType()];
111 16
112 16
                        foreach ($types as $type) {
113
                            $c = $type->getName();
114
                            if ($c === 'object' || $value instanceof $c) {
115
                                $object->{$property} = $value;
116
                                unset($data[$property]);
117
118
                                // go to next property
119
                                continue 2;
120
                            }
121
                        }
122
123
                        $relation = $relMap->getRelations()[$property] ?? null;
124
                        if ($relation !== null) {
125
                            $value = $relation->collect($relation->resolve($value, true));
126
                        }
127
                    }
128
                }
129
130
                $object->{$property} = $value;
131
                unset($data[$property]);
132
            }
133
        };
134
135
        foreach ($properties as $class => $props) {
136
            if ($class === '') {
137
                // Hydrate public properties
138
                $setter($object, $props, $data);
139
                continue;
140
            }
141
142
            Closure::bind($setter, null, $class)($object, $props, $data);
143
        }
144
    }
145
}
146