Completed
Push — master ( d466fc...57558f )
by Pavel
03:56
created

EntityHydrator   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 86.89%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 14
c 2
b 1
f 0
lcom 1
cbo 11
dl 0
loc 128
ccs 53
cts 61
cp 0.8689
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
C hydrateAssociation() 0 48 7
B hydarate() 0 42 6
1
<?php
2
3
namespace Bankiru\Api\Doctrine\Hydration;
4
5
use Bankiru\Api\Doctrine\EntityManager;
6
use Bankiru\Api\Doctrine\Exception\HydrationException;
7
use Bankiru\Api\Doctrine\Exception\MappingException;
8
use Bankiru\Api\Doctrine\Mapping\ApiMetadata;
9
use Bankiru\Api\Doctrine\Mapping\EntityMetadata;
10
use Doctrine\Common\Proxy\Proxy;
11
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
12
use Symfony\Component\PropertyAccess\PropertyAccessor;
13
14
class EntityHydrator
15
{
16
    /** @var  EntityMetadata */
17
    private $metadata;
18
    /** @var  EntityManager */
19
    private $manager;
20
21
    /**
22
     * EntityHydrator constructor.
23
     *
24
     * @param EntityManager $manager
25
     * @param ApiMetadata   $metadata
26
     */
27 12
    public function __construct(EntityManager $manager, ApiMetadata $metadata)
28
    {
29 12
        $this->manager  = $manager;
30 12
        $this->metadata = $metadata;
0 ignored issues
show
Documentation Bug introduced by
$metadata is of type object<Bankiru\Api\Doctrine\Mapping\ApiMetadata>, but the property $metadata was declared to be of type object<Bankiru\Api\Doctr...Mapping\EntityMetadata>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
31 12
    }
32
33
34
    /**
35
     * @param \StdClass   $source
36
     * @param object|null $entity
37
     *
38
     * @return object Hydrated object
39
     * @throws HydrationException
40
     */
41 12
    public function hydarate($source, $entity = null)
42
    {
43 12
        if (null === $entity) {
44 12
            $entity = $this->metadata->getReflectionClass()->newInstance();
45 12
        }
46
47 12
        $acessor = new PropertyAccessor();
48 12
        foreach ($this->metadata->getFieldNames() as $fieldName) {
49 12
            $property = $this->metadata->getReflectionProperty($fieldName);
50
51 12
            $apiField = $this->metadata->getApiFieldName($fieldName);
52
53
            try {
54 12
                $value = $acessor->getValue($source, $apiField);
55 12
            } catch (NoSuchPropertyException $exception) {
56 4
                if (!$this->metadata->getFieldMapping($fieldName)['nullable']) {
57
                    throw new HydrationException(
58
                        sprintf('Api field %s for property %s does not present in response', $apiField, $fieldName)
59
                    );
60
61
                }
62
63 4
                $property->setValue($entity, null);
64
65 4
                continue;
66
            }
67
68
            $type  =
69 12
                $this->manager->getConfiguration()->getTypeRegistry()->get($this->metadata->getTypeOfField($fieldName));
70 12
            $value = $type->fromApiValue($value);
71
72 12
            $property->setValue($entity, $value);
73 12
        }
74
75 12
        foreach ($this->metadata->getAssociationNames() as $fieldName) {
76 9
            $value    = $this->hydrateAssociation($fieldName, $entity, $source);
77 9
            $property = $this->metadata->getReflectionProperty($fieldName);
78 9
            $property->setValue($entity, $value);
79 12
        }
80
81 12
        return $entity;
82
    }
83
84
    /**
85
     * @param string    $field
86
     * @param \StdClass $source
87
     * @param object    $entity
88
     *
89
     * @return array|Proxy|object
90
     * @throws HydrationException
91
     * @throws MappingException
92
     */
93 9
    private function hydrateAssociation($field, $entity, $source)
94
    {
95 9
        $accessor        = new PropertyAccessor();
96 9
        $targetClassName = $this->metadata->getAssociationTargetClass($field);
97 9
        $mapping         = $this->metadata->getAssociationMapping($field);
98 9
        $targetPersister = $this->manager->getUnitOfWork()->getEntityPersister($targetClassName);
99 9
        $targetMetadata  = $this->manager->getClassMetadata($mapping['target']);
100 9
        $apiField        = $mapping['api_field'];
101 9
        $field           = $mapping['field'];
102
103
104 9
        if ($this->metadata->isSingleValuedAssociation($field)) {
105 9
            $identifiers = $this->metadata->getIdentifierValues($entity);
106 9
            if ($mapping['isOwningSide']) {
107
                try {
108 9
                    $value = $accessor->getValue($source, $apiField);
109 9
                } catch (NoSuchPropertyException $exception) {
110 8
                    if ($mapping['nullable']) {
111 8
                        return null;
112
                    }
113
114
                    throw new HydrationException(
115
                        sprintf('Api field %s for property %s does not present in response', $apiField, $field)
116
                    );
117
                }
118
119 4
                if ($targetMetadata->isIdentifierComposite()) {
120
                    throw new HydrationException('Composite references not supported');
121
                }
122
123 4
                $targetIdsNames = $targetMetadata->getIdentifierFieldNames();
124 4
                $targetIdName   = array_shift($targetIdsNames);
125
                $type           =
126 4
                    $this->manager->getConfiguration()
127 4
                                  ->getTypeRegistry()
128 4
                                  ->get($targetMetadata->getTypeOfField($targetIdName));
129 4
                $identifiers    = [$targetIdName => $type->fromApiValue($value)];
130 4
            }
131
132 4
            return $targetPersister->getToOneEntity($mapping, $entity, $identifiers);
133
        }
134
135 9
        if ($this->metadata->isCollectionValuedAssociation($field)) {
136 9
            return $targetPersister->getOneToManyCollection($mapping, $entity);
137
        }
138
139
        throw new MappingException('Invalid metadata association type');
140
    }
141
}
142