Passed
Pull Request — master (#14)
by Pavel
03:16
created

EntityHydrator   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 84.93%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 11
dl 0
loc 139
ccs 62
cts 73
cp 0.8493
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B hydarate() 0 45 6
C hydrateAssociation() 0 63 8
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
final class EntityHydrator implements Hydrator
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 16
    public function __construct(EntityManager $manager, ApiMetadata $metadata)
28
    {
29 16
        $this->manager  = $manager;
30 16
        $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 16
    }
32
33
    /** {@inheritdoc} */
34 16
    public function hydarate($source, $entity = null)
35
    {
36 16
        if (null === $entity) {
37 16
            $entity = $this->metadata->getReflectionClass()->newInstance();
38 16
        }
39
40 16
        $acessor = new PropertyAccessor();
41 16
        foreach ($this->metadata->getFieldNames() as $fieldName) {
42 16
            $property = $this->metadata->getReflectionProperty($fieldName);
43
44 16
            $apiField = $this->metadata->getApiFieldName($fieldName);
45
46
            try {
47 16
                $value = $acessor->getValue($source, $apiField);
48 16
            } catch (NoSuchPropertyException $exception) {
49 4
                if (!$this->metadata->getFieldMapping($fieldName)['nullable']) {
50
                    throw new HydrationException(
51
                        sprintf(
52
                            'Field %s for property %s does not present in dehydrated data',
53
                            $apiField,
54
                            $fieldName
55
                        )
56
                    );
57
                }
58
59 4
                $property->setValue($entity, null);
60
61 4
                continue;
62
            }
63
64
            $type  =
65 16
                $this->manager->getConfiguration()->getTypeRegistry()->get($this->metadata->getTypeOfField($fieldName));
66 16
            $value = $type->fromApiValue($value);
67
68 16
            $property->setValue($entity, $value);
69 16
        }
70
71 16
        foreach ($this->metadata->getAssociationNames() as $fieldName) {
72 11
            $value    = $this->hydrateAssociation($fieldName, $entity, $source);
73 11
            $property = $this->metadata->getReflectionProperty($fieldName);
74 11
            $property->setValue($entity, $value);
75 16
        }
76
77 16
        return $entity;
78
    }
79
80
    /**
81
     * @param string    $field
82
     * @param \StdClass $source
83
     * @param object    $entity
84
     *
85
     * @return array|Proxy|object
86
     * @throws HydrationException
87
     * @throws MappingException
88
     */
89 11
    private function hydrateAssociation($field, $entity, $source)
90
    {
91 11
        $accessor        = new PropertyAccessor();
92 11
        $targetClassName = $this->metadata->getAssociationTargetClass($field);
93 11
        $mapping         = $this->metadata->getAssociationMapping($field);
94 11
        $targetPersister = $this->manager->getUnitOfWork()->getEntityPersister($targetClassName);
95 11
        $targetMetadata  = $this->manager->getClassMetadata($mapping['target']);
96 11
        $apiField        = $mapping['api_field'];
97 11
        $field           = $mapping['field'];
98 11
        $oid             = spl_object_hash($entity);
99
100 11
        if ($this->metadata->isSingleValuedAssociation($field)) {
101 11
            $identifiers = $this->metadata->getIdentifierValues($entity);
102 11
            if ($mapping['isOwningSide']) {
103
                try {
104 11
                    $value = $accessor->getValue($source, $apiField);
105 11
                } catch (NoSuchPropertyException $exception) {
106 8
                    if ($mapping['nullable']) {
107 8
                        $this->manager->getUnitOfWork()->setOriginalEntityProperty($oid, $field, null);
108
109 8
                        return null;
110
                    }
111
112
                    throw new HydrationException(
113
                        sprintf('Api field %s for property %s does not present in response', $apiField, $field)
114
                    );
115
                }
116
117 6
                if (null === $value) {
118 1
                    return null;
119
                }
120
121 5
                if ($targetMetadata->isIdentifierComposite()) {
122
                    throw new HydrationException('Composite references not supported');
123
                }
124
125 5
                $targetIdsNames = $targetMetadata->getIdentifierFieldNames();
126 5
                $targetIdName   = array_shift($targetIdsNames);
127 5
                $type           = $this->manager
128 5
                    ->getConfiguration()
129 5
                    ->getTypeRegistry()
130 5
                    ->get($targetMetadata->getTypeOfField($targetIdName));
131
132 5
                $identifiers = [$targetIdName => $type->fromApiValue($value)];
133 5
            }
134
135 5
            $newValue = $targetPersister->getToOneEntity($mapping, $entity, $identifiers);
136
137 5
            $this->manager->getUnitOfWork()->setOriginalEntityProperty($oid, $field, $newValue);
138
139 5
            return $newValue;
140
        }
141
142 10
        if ($this->metadata->isCollectionValuedAssociation($field)) {
143 10
            $newValue = $targetPersister->getOneToManyCollection($mapping, $entity);
144
145 10
            $this->manager->getUnitOfWork()->setOriginalEntityProperty($oid, $field, $newValue);
146
147 10
            return $newValue;
148
        }
149
150
        throw new MappingException('Invalid metadata association type');
151
    }
152
}
153