Completed
Pull Request — master (#14)
by Pavel
28:57
created

EntityHydrator::hydrateAssociation()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 59
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 59
rs 7.5346
c 0
b 0
f 0
cc 7
eloc 37
nc 7
nop 3

How to fix   Long Method   

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
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
    public function __construct(EntityManager $manager, ApiMetadata $metadata)
28
    {
29
        $this->manager  = $manager;
30
        $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
    }
32
33
    /**
34
     * @param \StdClass   $source
35
     * @param object|null $entity
36
     *
37
     * @return object Hydrated object
38
     * @throws HydrationException
39
     */
40
    public function hydarate($source, $entity = null)
41
    {
42
        if (null === $entity) {
43
            $entity = $this->metadata->getReflectionClass()->newInstance();
44
        }
45
46
        $oid = spl_object_hash($entity);
0 ignored issues
show
Unused Code introduced by
$oid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
47
48
        $acessor = new PropertyAccessor();
49
        foreach ($this->metadata->getFieldNames() as $fieldName) {
50
            $property = $this->metadata->getReflectionProperty($fieldName);
51
52
            $apiField = $this->metadata->getApiFieldName($fieldName);
53
54
            try {
55
                $value = $acessor->getValue($source, $apiField);
56
            } catch (NoSuchPropertyException $exception) {
57
                if (!$this->metadata->getFieldMapping($fieldName)['nullable']) {
58
                    throw new HydrationException(
59
                        sprintf(
60
                            'Field %s for property %s does not present in dehydrated data',
61
                            $apiField,
62
                            $fieldName
63
                        )
64
                    );
65
                }
66
67
                $property->setValue($entity, null);
68
69
                continue;
70
            }
71
72
            $type  =
73
                $this->manager->getConfiguration()->getTypeRegistry()->get($this->metadata->getTypeOfField($fieldName));
74
            $value = $type->fromApiValue($value);
75
76
            $property->setValue($entity, $value);
77
        }
78
79
        foreach ($this->metadata->getAssociationNames() as $fieldName) {
80
            $value    = $this->hydrateAssociation($fieldName, $entity, $source);
81
            $property = $this->metadata->getReflectionProperty($fieldName);
82
            $property->setValue($entity, $value);
83
        }
84
85
        return $entity;
86
    }
87
88
    /**
89
     * @param string    $field
90
     * @param \StdClass $source
91
     * @param object    $entity
92
     *
93
     * @return array|Proxy|object
94
     * @throws HydrationException
95
     * @throws MappingException
96
     */
97
    private function hydrateAssociation($field, $entity, $source)
98
    {
99
        $accessor        = new PropertyAccessor();
100
        $targetClassName = $this->metadata->getAssociationTargetClass($field);
101
        $mapping         = $this->metadata->getAssociationMapping($field);
102
        $targetPersister = $this->manager->getUnitOfWork()->getEntityPersister($targetClassName);
103
        $targetMetadata  = $this->manager->getClassMetadata($mapping['target']);
104
        $apiField        = $mapping['api_field'];
105
        $field           = $mapping['field'];
106
        $oid             = spl_object_hash($entity);
107
108
        if ($this->metadata->isSingleValuedAssociation($field)) {
109
            $identifiers = $this->metadata->getIdentifierValues($entity);
110
            if ($mapping['isOwningSide']) {
111
                try {
112
                    $value = $accessor->getValue($source, $apiField);
113
                } catch (NoSuchPropertyException $exception) {
114
                    if ($mapping['nullable']) {
115
                        $this->manager->getUnitOfWork()->setOriginalEntityProperty($oid, $field, null);
116
117
                        return null;
118
                    }
119
120
                    throw new HydrationException(
121
                        sprintf('Api field %s for property %s does not present in response', $apiField, $field)
122
                    );
123
                }
124
125
                if ($targetMetadata->isIdentifierComposite()) {
126
                    throw new HydrationException('Composite references not supported');
127
                }
128
129
                $targetIdsNames = $targetMetadata->getIdentifierFieldNames();
130
                $targetIdName   = array_shift($targetIdsNames);
131
                $type           = $this->manager
132
                    ->getConfiguration()
133
                    ->getTypeRegistry()
134
                    ->get($targetMetadata->getTypeOfField($targetIdName));
135
136
                $identifiers = [$targetIdName => $type->fromApiValue($value)];
137
            }
138
139
            $newValue = $targetPersister->getToOneEntity($mapping, $entity, $identifiers);
140
141
            $this->manager->getUnitOfWork()->setOriginalEntityProperty($oid, $field, $newValue);
142
143
            return $newValue;
144
        }
145
146
        if ($this->metadata->isCollectionValuedAssociation($field)) {
147
            $newValue = $targetPersister->getOneToManyCollection($mapping, $entity);
148
149
            $this->manager->getUnitOfWork()->setOriginalEntityProperty($oid, $field, $newValue);
150
151
            return $newValue;
152
        }
153
154
        throw new MappingException('Invalid metadata association type');
155
    }
156
}
157