Completed
Pull Request — master (#317)
by Joel
05:56
created

DoctrineWriter::writeItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1.0046

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
crap 1.0046
1
<?php
2
3
namespace Ddeboer\DataImport\Writer;
4
5
use Ddeboer\DataImport\Writer;
6
use Doctrine\DBAL\Logging\SQLLogger;
7
use Doctrine\ORM\EntityManagerInterface;
8
use Doctrine\ORM\EntityRepository;
9
use Doctrine\ORM\Mapping\ClassMetadata;
10
11
/**
12
 * A bulk Doctrine writer
13
 *
14
 * See also the {@link http://www.doctrine-project.org/docs/orm/2.1/en/reference/batch-processing.html Doctrine documentation}
15
 * on batch processing.
16
 *
17
 * @author David de Boer <[email protected]>
18
 */
19
class DoctrineWriter implements Writer, FlushableWriter
20
{
21
    /**
22
     * @var EntityManagerInterface
23
     */
24
    protected $entityManager;
25
26
    /**
27
     * @var string
28
     */
29
    protected $entityName;
30
31
    /**
32
     * @var EntityRepository
33
     */
34
    protected $entityRepository;
35
36
    /**
37
     * @var ClassMetadata
38
     */
39
    protected $entityMetadata;
40
41
    /**
42
     * Original Doctrine logger
43
     *
44
     * @var SQLLogger
45
     */
46
    protected $originalLogger;
47
48
    /**
49
     * Whether to truncate the table first
50
     *
51
     * @var boolean
52
     */
53
    protected $truncate = true;
54
55
    /**
56
     * List of fields used to lookup an entity
57
     *
58
     * @var array
59
     */
60
    protected $lookupFields = array();
61
62
    /**
63
     * @param EntityManagerInterface $entityManager
64
     * @param string                 $entityName
65
     * @param string|array           $index Field or fields to find current entities by
66
     */
67 4
    public function __construct(EntityManagerInterface $entityManager, $entityName, $index = null)
68
    {
69 4
        $this->entityManager = $entityManager;
70 4
        $this->entityRepository = $entityManager->getRepository($entityName);
71 4
        $this->entityMetadata = $entityManager->getClassMetadata($entityName);
72
        //translate entityName in case a namespace alias is used
73 4
        $this->entityName = $this->entityMetadata->getName();
74 4
        if ($index) {
75
            if (is_array($index)) {
76
                $this->lookupFields = $index;
77
            } else {
78
                $this->lookupFields = [$index];
79
            }
80
        }
81 4
    }
82
83
    /**
84
     * @return boolean
85
     */
86
    public function getTruncate()
87
    {
88
        return $this->truncate;
89
    }
90
91
    /**
92
     * Set whether to truncate the table first
93
     *
94
     * @param boolean $truncate
95
     *
96
     * @return $this
97
     */
98
    public function setTruncate($truncate)
99
    {
100
        $this->truncate = $truncate;
101
102
        return $this;
103
    }
104
105
    /**
106
     * Disable truncation
107
     *
108
     * @return $this
109
     */
110
    public function disableTruncate()
111
    {
112
        $this->truncate = false;
113
114
        return $this;
115
    }
116
117
    /**
118
     * Disable Doctrine logging
119
     *
120
     * @return $this
121
     */
122
    public function prepare()
123
    {
124
        $this->disableLogging();
125
126
        if (true === $this->truncate) {
127
            $this->truncateTable();
128
        }
129
    }
130
131
    /**
132
     * Return a new instance of the entity
133
     *
134
     * @return object
135
     */
136 3
    protected function getNewInstance()
137
    {
138 3
        $className = $this->entityMetadata->getName();
139
140 3
        if (class_exists($className) === false) {
141
            throw new \Exception('Unable to create new instance of ' . $className);
142
        }
143
144 3
        return new $className;
145
    }
146
147
    /**
148
     * Re-enable Doctrine logging
149
     *
150
     * @return $this
151
     */
152 1
    public function finish()
153
    {
154 1
        $this->flush();
155 1
        $this->reEnableLogging();
156 1
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 3
    public function writeItem(array $item)
162
    {
163 3
        $entity = $this->findOrCreateItem($item);
164
165 3
        $this->loadAssociationObjectsToEntity($item, $entity);
166 3
        $this->updateEntity($item, $entity);
167
168 3
        $this->entityManager->persist($entity);
169
    }
170
171
    /**
172
     * @param array  $item
173
     * @param object $entity
174
     */
175 3
    protected function updateEntity(array $item, $entity)
176
    {
177 3
        $fieldNames = array_merge($this->entityMetadata->getFieldNames(), $this->entityMetadata->getAssociationNames());
178 3
        foreach ($fieldNames as $fieldName) {
179 3
            $value = null;
180 3
            if (isset($item[$fieldName])) {
181 3
                $value = $item[$fieldName];
182 3
            } elseif (method_exists($item, 'get' . ucfirst($fieldName))) {
183
                $value = $item->{'get' . ucfirst($fieldName)};
184
            }
185
186 3
            if (null === $value) {
187
                continue;
188
            }
189
190 3
            if (!($value instanceof \DateTime)
191 3
                || $value != $this->entityMetadata->getFieldValue($entity, $fieldName)
192 3
            ) {
193 3
                $this->entityMetadata->setFieldValue($entity, $fieldName, $value);
194 3
            }
195 3
        }
196 3
    }
197
198
    /**
199
     * Add the associated objects in case the item have for persist its relation
200
     *
201
     * @param array  $item
202
     * @param object $entity
203
     */
204 3
    protected function loadAssociationObjectsToEntity(array $item, $entity)
205
    {
206 3
        foreach ($this->entityMetadata->getAssociationMappings() as $associationMapping) {
207
208 3
            $value = null;
209 3
            if (isset($item[$associationMapping['fieldName']]) && !is_object($item[$associationMapping['fieldName']])) {
210 1
                $value = $this->entityManager->getReference($associationMapping['targetEntity'], $item[$associationMapping['fieldName']]);
211 1
            }
212
213 3
            if (null === $value) {
214 3
                continue;
215
            }
216
217
            $this->entityMetadata->setFieldValue($entity, $associationMapping['fieldName'], $value);
218 3
        }
219 3
    }
220
221
    /**
222
     * Truncate the database table for this writer
223
     */
224
    protected function truncateTable()
225
    {
226
        $tableName = $this->entityMetadata->table['name'];
227
        $connection = $this->entityManager->getConnection();
228
        $query = $connection->getDatabasePlatform()->getTruncateTableSQL($tableName, true);
229
        $connection->executeQuery($query);
230
    }
231
232
    /**
233
     * Disable Doctrine logging
234
     */
235
    protected function disableLogging()
236
    {
237
        $config = $this->entityManager->getConnection()->getConfiguration();
238
        $this->originalLogger = $config->getSQLLogger();
239
        $config->setSQLLogger(null);
240
    }
241
242
    /**
243
     * Re-enable Doctrine logging
244
     */
245 1
    protected function reEnableLogging()
246
    {
247 1
        $config = $this->entityManager->getConnection()->getConfiguration();
248 1
        $config->setSQLLogger($this->originalLogger);
249 1
    }
250
251
    /**
252
     * Finds existing entity or create a new instance
253
     *
254
     * @param array $item
255
     */
256 3
    protected function findOrCreateItem(array $item)
257
    {
258 3
        $entity = null;
259
        // If the table was not truncated to begin with, find current entity
260
        // first
261 3
        if (false === $this->truncate) {
262
            if (!empty($this->lookupFields)) {
263
                $lookupConditions = array();
264
                foreach ($this->lookupFields as $fieldName) {
265
                    $lookupConditions[$fieldName] = $item[$fieldName];
266
                }
267
                $entity = $this->entityRepository->findOneBy(
268
                    $lookupConditions
269
                );
270
            } else {
271
                $entity = $this->entityRepository->find(current($item));
272
            }
273
        }
274
275 3
        if (!$entity) {
276 3
            return $this->getNewInstance();
277
        }
278
279
        return $entity;
280
    }
281
282
    /**
283
     * Flush and clear the entity manager
284
     */
285 1
    public function flush()
286
    {
287 1
        $this->entityManager->flush();
288 1
        $this->entityManager->clear($this->entityName);
289 1
    }
290
}
291