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

DoctrineInsertUpdateLoader::clearLogs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
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