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

DoctrineInsertUpdateLoader::getLogs()   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
            'callback' => $identifierCallback,
68
            'identifier' => $identifierProperty,
69
            'properties' => $entityProperties
70
        ];
71
72
        return $this;
73 1
    }
74
75 1
    /**
76
     * @throws \Exception
77 1
     */
78 1
    public function load(array $data)
79
    {
80 1
        $this->entityManager->beginTransaction();
81 1
        try {
82
            foreach ($data as $object) {
83
                $this->processObject($object);
84
            }
85
            $this->entityManager->flush();
86 1
            $this->entityManager->commit();
87
        } catch (\Exception $e) {
88
            $this->entityManager->rollback();
89
90
            throw new \Exception('EXCEPTION LOADER : ' . $e->getMessage());
91
        }
92
    }
93
94 1
    /**
95
     * @param  ImportableInterface $object
96 1
     * @return ImportableInterface
97
     * @throws \Exception
98
     * @throws \TypeError
99 1
     */
100
    protected function processObject($object)
101
    {
102 1
        $objectClass = get_class($object);
103 1
        if (!isset($this->entitiesToProcess[$objectClass])) {
104 1
            throw new EntityTypeNotHandledException($objectClass);
105 1
        }
106
        $identifier = $this->entitiesToProcess[$objectClass]['callback']($object);
107 1
108
        //Replace relations by their reference
109
        foreach ($this->entitiesToProcess[$objectClass]['properties'] as $property) {
110 1
            $propertyValue = $this->accessor->getValue($object, $property);
111 1
            if ($this->isEntityRelation($propertyValue)) {
112
                $relation = $propertyValue; //better understanding
113 1
114
                if (!isset($this->entitiesToProcess[get_class($relation)])) {
115 1
                    throw new EntityTypeNotHandledException(get_class($relation));
116 1
                }
117
                $relationIdentifier = $this->entitiesToProcess[get_class($relation)]['callback']($relation);
118 1
                if (!isset($this->references[$relationIdentifier])) {
119
                    //new relation should be processed before
120 1
                    $this->processObject($relation);
121 1
                }
122 1
                $this->accessor->setValue(
123 1
                    $object,
124
                    $property,
125
                    $this->references[$relationIdentifier]
126 1
                );
127 1
            } elseif ($propertyValue instanceof \Traversable) {
128
                foreach ($propertyValue as $k => $v) {
129 1
                    if ($this->isEntityRelation($v)) {
130
                        if (!isset($this->entitiesToProcess[get_class($v)])) {
131 1
                            throw new EntityTypeNotHandledException(get_class($v));
132
                        }
133
                        $relationIdentifier = $this->entitiesToProcess[get_class($v)]['callback']($v);
134 1
                        if (!isset($this->references[$relationIdentifier])) {
135 1
                            //new relation should be processed before
136
                            $this->processObject($v);
137
                        }
138
                        $propertyValue[$k] = $this->references[$relationIdentifier];
139
                    }
140
                }
141
                $this->accessor->setValue(
142 1
                    $object,
143 1
                    $property,
144 1
                    $propertyValue
145
                );
146 1
            }
147 1
        }
148 1
149
        $dbObject = null;
150 1
        if (!is_null($this->entitiesToProcess[$objectClass]['identifier'])) {
151 1
            // todo amélioration récupérer directement tous dbObject dont l'identifier match ceux présent dans $data
152 1
            $dbObject = $this->entityManager->getRepository($objectClass)->findOneBy([$this->entitiesToProcess[$objectClass]['identifier'] => $identifier]);
153
        }
154
        if ($dbObject === null) {
155 1
            if (!$object->isImported()) {
156 1
                $object->setImportedAt(new \DateTime());
157
            }
158 1
            $this->entityManager->persist($object);
159 1
            if (!is_null($identifier)) {
160
                $this->references[$identifier] = $object;
161 1
            }
162
163
            if (isset($this->logs[$objectClass])) {
164 1
                $this->logs[$objectClass]['nb_created']++;
165
            } else {
166
                $this->logs[$objectClass] = [
167
                    'nb_created' => 1,
168
                    'nb_updated' => 0,
169
                ];
170
            }
171
        } else {
172
            foreach ($this->entitiesToProcess[$objectClass]['properties'] as $property) {
173 1
                $this->accessor->setValue($dbObject, $property, $this->accessor->getValue($object, $property));
174
            }
175 1
            if (!$dbObject->isImported()) {
176
                $dbObject->setImportedAt(new \DateTime());
177
            }
178
            $this->references[$identifier] = $dbObject;
179
180
            if (isset($this->logs[$objectClass])) {
181
                $this->logs[$objectClass]['nb_updated']++;
182
            } else {
183
                $this->logs[$objectClass] = [
184
                    'nb_created' => 0,
185
                    'nb_updated' => 1,
186
                ];
187
            }
188
        }
189
190
        return $object;
191
    }
192
193
    /**
194
     * Check if $propertyValue is an entity relation to process
195
     *
196
     * @param  mixed $propertyValue
197
     * @return bool
198
     */
199
    protected function isEntityRelation($propertyValue)
200
    {
201
        return (is_object($propertyValue) && !($propertyValue instanceof \DateTime) && !($propertyValue instanceof \Traversable));
202
    }
203
204
    public function getLogs(): array
205
    {
206
        return $this->logs;
207
    }
208
209
    public function clearLogs(): void
210
    {
211
        $this->logs = [];
212
    }
213
}
214