Completed
Push — master ( 38fe2d...4ed937 )
by Peter
12s
created

ArrayHydrator   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 183
Duplicated Lines 3.28 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 4
dl 6
loc 183
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A hydrate() 0 13 4
A setHydrateAssociationReferences() 0 4 1
B hydrateProperties() 0 27 5
B hydrateAssociations() 6 17 5
A hydrateToOneAssociation() 0 11 2
B hydrateToManyAssociation() 0 19 5
A setProperty() 0 8 2
A fetchAssociationEntity() 0 8 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
namespace pmill\Doctrine\Hydrator;
3
4
use Doctrine\DBAL\Types\Type;
5
use Doctrine\ORM\EntityManager;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use Exception;
8
9
class ArrayHydrator
10
{
11
    /**
12
     * @var EntityManager
13
     */
14
    protected $entityManager;
15
16
    /**
17
     * @var bool
18
     */
19
    protected $hydrateAssociationReferences = true;
20
21
    /**
22
     * @param EntityManager $entityManager
23
     */
24
    public function __construct(EntityManager $entityManager)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
25
    {
26
        $this->entityManager = $entityManager;
27
    }
28
29
    /**
30
     * @param $entity
31
     * @param array $data
32
     * @return mixed|object
33
     * @throws Exception
34
     */
35
    public function hydrate($entity, array $data)
36
    {
37
        if (is_string($entity) && class_exists($entity)) {
38
            $entity = new $entity;
39
        }
40
        elseif (!is_object($entity)) {
41
            throw new Exception('Entity passed to ArrayHydrator::hydrate() must be a class name or entity object');
42
        }
43
44
        $entity = $this->hydrateProperties($entity, $data);
45
        $entity = $this->hydrateAssociations($entity, $data);
46
        return $entity;
47
    }
48
49
    /**
50
     * @param boolean $hydrateAssociationReferences
51
     */
52
    public function setHydrateAssociationReferences($hydrateAssociationReferences)
53
    {
54
        $this->hydrateAssociationReferences = $hydrateAssociationReferences;
55
    }
56
57
    /**
58
     * @param $entity
59
     * @param $data
60
     * @return object
61
     */
62
    protected function hydrateProperties($entity, $data)
63
    {
64
        $reflectionObject = new \ReflectionObject($entity);
65
66
        $metaData = $this->entityManager->getClassMetadata(get_class($entity));
67
        
68
        $platform = $this->entityManager->getConnection()
69
                                        ->getDatabasePlatform();
70
        
71
        foreach ($metaData->columnNames as $propertyName) {
72
            if (isset($data[$propertyName]) && !in_array($propertyName, $metaData->identifier)) {
73
                $value = $data[$propertyName];
74
                
75
                if (array_key_exists('type', $metaData->fieldMappings[$propertyName])) {
76
                    $fieldType = $metaData->fieldMappings[$propertyName]['type'];
77
                    
78
                    $type = Type::getType($fieldType);
79
                    
80
                    $value = $type->convertToPHPValue($value, $platform);
81
                }
82
83
                $entity = $this->setProperty($entity, $propertyName, $value, $reflectionObject);
84
            }
85
        }
86
87
        return $entity;
88
    }
89
90
    /**
91
     * @param $entity
92
     * @param $data
93
     * @return mixed
94
     */
95
    protected function hydrateAssociations($entity, $data)
96
    {
97
        $metaData = $this->entityManager->getClassMetadata(get_class($entity));
98
        foreach ($metaData->associationMappings as $propertyName => $mapping) {
99
            if (isset($data[$propertyName])) {
100 View Code Duplication
                if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
                    $entity = $this->hydrateToOneAssociation($entity, $propertyName, $mapping, $data[$propertyName]);
102
                }
103
104 View Code Duplication
                if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::MANY_TO_MANY])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
                    $entity = $this->hydrateToManyAssociation($entity, $propertyName, $mapping, $data[$propertyName]);
106
                }
107
            }
108
        }
109
110
        return $entity;
111
    }
112
113
    /**
114
     * @param $entity
115
     * @param $propertyName
116
     * @param $mapping
117
     * @param $value
118
     * @return mixed
119
     */
120
    protected function hydrateToOneAssociation($entity, $propertyName, $mapping, $value)
121
    {
122
        $reflectionObject = new \ReflectionObject($entity);
123
124
        $toOneAssociationObject = $this->fetchAssociationEntity($mapping['targetEntity'], $value);
125
        if (!is_null($toOneAssociationObject)) {
126
            $entity = $this->setProperty($entity, $propertyName, $toOneAssociationObject, $reflectionObject);
127
        }
128
129
        return $entity;
130
    }
131
132
    /**
133
     * @param $entity
134
     * @param $propertyName
135
     * @param $mapping
136
     * @param $value
137
     * @return mixed
138
     */
139
    protected function hydrateToManyAssociation($entity, $propertyName, $mapping, $value)
140
    {
141
        $reflectionObject = new \ReflectionObject($entity);
142
        $values = is_array($value) ? $value : [$value];
143
144
        $assocationObjects = [];
145
        foreach ($values as $value) {
146
            if (is_array($value)) {
147
                $assocationObjects[] = $this->hydrate($mapping['targetEntity'], $value);
148
            }
149
            elseif ($associationObject = $this->fetchAssociationEntity($mapping['targetEntity'], $value)) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $associationObject is correct as $this->fetchAssociationE...targetEntity'], $value) (which targets pmill\Doctrine\Hydrator\...etchAssociationEntity()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
150
                $assocationObjects[] = $associationObject;
151
            }
152
        }
153
154
        $entity = $this->setProperty($entity, $propertyName, $assocationObjects, $reflectionObject);
155
156
        return $entity;
157
    }
158
159
    /**
160
     * @param $entity
161
     * @param $propertyName
162
     * @param $value
163
     * @param null $reflectionObject
164
     * @return mixed
165
     */
166
    protected function setProperty($entity, $propertyName, $value, $reflectionObject = null)
167
    {
168
        $reflectionObject = is_null($reflectionObject) ? new \ReflectionObject($entity) : $reflectionObject;
169
        $property = $reflectionObject->getProperty($propertyName);
170
        $property->setAccessible(true);
171
        $property->setValue($entity, $value);
172
        return $entity;
173
    }
174
175
    /**
176
     * @param $className
177
     * @param $id
178
     * @return bool|\Doctrine\Common\Proxy\Proxy|null|object
179
     * @throws \Doctrine\ORM\ORMException
180
     * @throws \Doctrine\ORM\OptimisticLockException
181
     * @throws \Doctrine\ORM\TransactionRequiredException
182
     */
183
    protected function fetchAssociationEntity($className, $id)
184
    {
185
        if ($this->hydrateAssociationReferences) {
186
            return $this->entityManager->getReference($className, $id);
187
        }
188
189
        return $this->entityManager->find($className, $id);
190
    }
191
}
192