Passed
Pull Request — master (#30)
by Mathieu
02:11
created

DoctrineInsertUpdateLoader::processObject()   F

Complexity

Conditions 19
Paths 259

Size

Total Lines 92
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 19.0451

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 19
eloc 58
c 2
b 0
f 0
nc 259
nop 1
dl 0
loc 92
ccs 38
cts 40
cp 0.95
crap 19.0451
rs 2.9458

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
namespace Smart\EtlBundle\Loader;
4
5
use Doctrine\ORM\EntityManager;
6
use Smart\EtlBundle\Entity\ImportableInterface;
7
use Smart\EtlBundle\Exception\Loader\EntityTypeNotHandledException;
8
use Smart\EtlBundle\Exception\Loader\EntityAlreadyRegisteredException;
9
use Symfony\Component\PropertyAccess\PropertyAccess;
10
use Symfony\Component\PropertyAccess\PropertyAccessor;
11
12
/**
13
 * Nicolas Bastien <[email protected]>
14
 */
15
class DoctrineInsertUpdateLoader implements LoaderInterface
16
{
17
    /**
18
     * @var EntityManager
19
     */
20
    protected $entityManager;
21
22
    /**
23
     * @var array
24
     */
25
    protected $references;
26
27
    /**
28
     * @var PropertyAccessor
29
     */
30
    protected $accessor;
31
32
    /**
33
     * List of entities to extract
34
     * [
35
     *      'class' => []
36
     * ]
37
     * @var array
38
     */
39
    protected $entitiesToProcess = [];
40
41 1
    /**
42
     * @var array
43 1
     */
44 1
    protected $logs = [];
45 1
46
    public function __construct($entityManager)
47
    {
48
        $this->entityManager = $entityManager;
49
        $this->accessor = PropertyAccess::createPropertyAccessor();
50
    }
51
52
    /**
53
     * @param string $entityClass
54 1
     * @param callback $identifierCallback
55
     * @param string $identifierProperty : if null this entity will be always insert
56 1
     * @param array $entityProperties properties to synchronize
57
     * @return $this
58
     */
59
    public function addEntityToProcess($entityClass, $identifierCallback, $identifierProperty, array $entityProperties = [])
60 1
    {
61 1
        if (isset($this->entitiesToProcess[$entityClass])) {
62 1
            throw new EntityAlreadyRegisteredException($entityClass);
63 1
        }
64 1
65
        $this->entitiesToProcess[$entityClass] = [
66
            'class' => $entityClass,
67 1
            // todo refacto enlever le param callback et passer directement par l'accessor getValue
68
            'callback' => $identifierCallback,
69
            'identifier' => $identifierProperty,
70
            'properties' => $entityProperties
71
        ];
72
73 1
        return $this;
74
    }
75 1
76
    /**
77 1
     * @throws \Exception
78 1
     */
79
    public function load(array $data)
80 1
    {
81 1
        $this->entityManager->beginTransaction();
82
        try {
83
            foreach ($data as $object) {
84
                $this->processObject($object);
85
            }
86 1
            $this->entityManager->flush();
87
            $this->entityManager->commit();
88
        } catch (\Exception $e) {
89
            $this->entityManager->rollback();
90
91
            throw new \Exception('EXCEPTION LOADER : ' . $e->getMessage());
92
        }
93
    }
94 1
95
    /**
96 1
     * @param  ImportableInterface $object
97
     * @return ImportableInterface
98
     * @throws \Exception
99 1
     * @throws \TypeError
100
     */
101
    protected function processObject($object)
102 1
    {
103 1
        $objectClass = get_class($object);
104 1
        if (!isset($this->entitiesToProcess[$objectClass])) {
105 1
            throw new EntityTypeNotHandledException($objectClass);
106
        }
107 1
        $identifier = $this->entitiesToProcess[$objectClass]['callback']($object);
108
109
        //Replace relations by their reference
110 1
        foreach ($this->entitiesToProcess[$objectClass]['properties'] as $property) {
111 1
            $propertyValue = $this->accessor->getValue($object, $property);
112
            if ($this->isEntityRelation($propertyValue)) {
113 1
                $relation = $propertyValue; //better understanding
114
115 1
                if (!isset($this->entitiesToProcess[get_class($relation)])) {
116 1
                    throw new EntityTypeNotHandledException(get_class($relation));
117
                }
118 1
                $relationIdentifier = $this->entitiesToProcess[get_class($relation)]['callback']($relation);
119
                if (!isset($this->references[$relationIdentifier])) {
120 1
                    //new relation should be processed before
121 1
                    $this->processObject($relation);
122 1
                }
123 1
                $this->accessor->setValue(
124
                    $object,
125
                    $property,
126 1
                    $this->references[$relationIdentifier]
127 1
                );
128
            } elseif ($propertyValue instanceof \Traversable) {
129 1
                foreach ($propertyValue as $k => $v) {
130
                    if ($this->isEntityRelation($v)) {
131 1
                        if (!isset($this->entitiesToProcess[get_class($v)])) {
132
                            throw new EntityTypeNotHandledException(get_class($v));
133
                        }
134 1
                        $relationIdentifier = $this->entitiesToProcess[get_class($v)]['callback']($v);
135 1
                        if (!isset($this->references[$relationIdentifier])) {
136
                            //new relation should be processed before
137
                            $this->processObject($v);
138
                        }
139
                        $propertyValue[$k] = $this->references[$relationIdentifier];
140
                    }
141
                }
142 1
                $this->accessor->setValue(
143 1
                    $object,
144 1
                    $property,
145
                    $propertyValue
146 1
                );
147 1
            }
148 1
        }
149
150 1
        $dbObject = null;
151 1
        if (!is_null($this->entitiesToProcess[$objectClass]['identifier'])) {
152 1
            // todo amélioration récupérer directement tous dbObject dont l'identifier match ceux présent dans $data
153
            $dbObject = $this->entityManager->getRepository($objectClass)->findOneBy([$this->entitiesToProcess[$objectClass]['identifier'] => $identifier]);
154
        }
155 1
        if ($dbObject === null) {
156 1
            if (!$object->isImported()) {
157
                $object->setImportedAt(new \DateTime());
158 1
            }
159 1
            $this->entityManager->persist($object);
160
            if (!is_null($identifier)) {
161 1
                $this->references[$identifier] = $object;
162
            }
163
164 1
            if (isset($this->logs[$objectClass])) {
165
                $this->logs[$objectClass]['nb_created']++;
166
            } else {
167
                $this->logs[$objectClass] = [
168
                    'nb_created' => 1,
169
                    'nb_updated' => 0,
170
                ];
171
            }
172
        } else {
173 1
            // todo valider si aucun changement
174
            foreach ($this->entitiesToProcess[$objectClass]['properties'] as $property) {
175 1
                $this->accessor->setValue($dbObject, $property, $this->accessor->getValue($object, $property));
176
            }
177
            if (!$dbObject->isImported()) {
178
                $dbObject->setImportedAt(new \DateTime());
179
            }
180
            $this->references[$identifier] = $dbObject;
181
182
            if (isset($this->logs[$objectClass])) {
183
                $this->logs[$objectClass]['nb_updated']++;
184
            } else {
185
                $this->logs[$objectClass] = [
186
                    'nb_created' => 0,
187
                    'nb_updated' => 1,
188
                ];
189
            }
190
        }
191
192
        return $object;
193
    }
194
195
    /**
196
     * Check if $propertyValue is an entity relation to process
197
     *
198
     * @param  mixed $propertyValue
199
     * @return bool
200
     */
201
    protected function isEntityRelation($propertyValue)
202
    {
203
        return (is_object($propertyValue) && !($propertyValue instanceof \DateTime) && !($propertyValue instanceof \Traversable));
204
    }
205
206
    public function getLogs(): array
207
    {
208
        return $this->logs;
209
    }
210
211
    public function clearLogs(): void
212
    {
213
        $this->logs = [];
214
    }
215
}
216