Passed
Pull Request — master (#4)
by Alex
02:54
created

PersistService::refresh()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 11
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\LaminasDoctrine\Repository\Persistence;
6
7
use Arp\Entity\EntityInterface;
8
use Arp\LaminasDoctrine\Repository\Persistence\Exception\PersistenceException;
9
use Doctrine\ORM\EntityManagerInterface;
10
use Psr\Log\LoggerInterface;
11
12
class PersistService implements PersistServiceInterface
13
{
14
    /**
15
     * @var string
16
     */
17
    protected string $entityName;
18
19
    /**
20
     * @var EntityManagerInterface
21
     */
22
    protected EntityManagerInterface $entityManager;
23
24
    /**
25
     * @var LoggerInterface
26
     */
27
    protected LoggerInterface $logger;
28
29
    /**
30
     * @param string                 $entityName
31
     * @param EntityManagerInterface $entityManager
32
     * @param LoggerInterface        $logger
33
     */
34
    public function __construct(
35
        string $entityName,
36
        EntityManagerInterface $entityManager,
37
        LoggerInterface $logger
38
    ) {
39
        $this->entityName = $entityName;
40
        $this->entityManager = $entityManager;
41
        $this->logger = $logger;
42
    }
43
44
    /**
45
     * Return the full qualified class name of the entity.
46
     *
47
     * @return string
48
     */
49
    public function getEntityName(): string
50
    {
51
        return $this->entityName;
52
    }
53
54
    /**
55
     * @param EntityInterface          $entity
56
     * @param array<string|int, mixed> $options
57
     *
58
     * @return EntityInterface
59
     *
60
     * @throws PersistenceException
61
     */
62
    public function save(EntityInterface $entity, array $options = []): EntityInterface
63
    {
64
        if ($entity->hasId()) {
65
            return $this->update($entity, $options);
66
        }
67
        return $this->insert($entity, $options);
68
    }
69
70
    /**
71
     * @param iterable<EntityInterface> $collection The collection of entities that should be saved
72
     * @param array<string|int, mixed>  $options    the optional save options
73
     *
74
     * @return iterable<EntityInterface>
75
     *
76
     * @throws PersistenceException
77
     */
78
    public function saveCollection(iterable $collection, array $options = []): iterable
79
    {
80
        $transaction = (bool)($options['transaction'] ?? true);
81
        $flush = (bool)($options['flush'] ?? true);
82
83
        try {
84
            if ($transaction) {
85
                $this->beginTransaction();
86
            }
87
88
            $saveOptions = array_replace_recursive(
89
                [
90
                    'flush' => !$flush,
91
                    'transaction' => !$transaction
92
                ],
93
                $options['entity_options'] ?? []
94
            );
95
96
            foreach ($collection as $entity) {
97
                $this->save($entity, $saveOptions);
98
            }
99
100
            if ($flush) {
101
                $this->entityManager->flush();
102
            }
103
104
            if ($transaction) {
105
                $this->commitTransaction();
106
            }
107
108
            return $collection;
109
        } catch (PersistenceException $e) {
110
            if ($transaction) {
111
                $this->rollbackTransaction();
112
            }
113
            throw $e;
114
        } catch (\Exception $e) {
115
            if ($transaction) {
116
                $this->rollbackTransaction();
117
            }
118
119
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
120
121
            throw new PersistenceException(
122
                sprintf('Failed to save collection of type \'%s\'', $this->entityName),
123
                $e->getCode(),
124
                $e
125
            );
126
        }
127
    }
128
129
    /**
130
     * @param EntityInterface          $entity
131
     * @param array<string|int, mixed> $options
132
     *
133
     * @return EntityInterface
134
     *
135
     * @throws PersistenceException
136
     */
137
    protected function update(EntityInterface $entity, array $options = []): EntityInterface
138
    {
139
        $transaction = (bool)($options['transaction'] ?? false);
140
        $flush = (bool)($options['flush'] ?? true);
141
142
        try {
143
            if ($transaction) {
144
                $this->beginTransaction();
145
            }
146
147
            if ($flush) {
148
                $this->entityManager->flush();
149
            }
150
151
            if ($transaction) {
152
                $this->commitTransaction();
153
            }
154
155
            return $entity;
156
        } catch (PersistenceException $e) {
157
            $this->rollbackTransaction();
158
            throw $e;
159
        } catch (\Exception $e) {
160
            if ($transaction) {
161
                $this->rollbackTransaction();
162
            }
163
164
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
165
166
            throw new PersistenceException(
167
                sprintf('Failed to update entity of type \'%s\'', $this->entityName),
168
                $e->getCode(),
169
                $e
170
            );
171
        }
172
    }
173
174
    /**
175
     * @param EntityInterface          $entity
176
     * @param array<string|int, mixed> $options
177
     *
178
     * @return EntityInterface
179
     *
180
     * @throws PersistenceException
181
     */
182
    protected function insert(EntityInterface $entity, array $options = []): EntityInterface
183
    {
184
        $transaction = (bool)($options['transaction'] ?? false);
185
        $flush = (bool)($options['flush'] ?? true);
186
187
        try {
188
            $this->entityManager->persist($entity);
189
190
            if ($transaction) {
191
                $this->beginTransaction();
192
            }
193
194
            if ($flush) {
195
                $this->flush();
196
            }
197
198
            if ($transaction) {
199
                $this->commitTransaction();
200
            }
201
202
            return $entity;
203
        } catch (PersistenceException $e) {
204
            if ($transaction) {
205
                $this->rollbackTransaction();
206
            }
207
            throw $e;
208
        } catch (\Exception $e) {
209
            if ($transaction) {
210
                $this->rollbackTransaction();
211
            }
212
213
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
214
215
            throw new PersistenceException(
216
                sprintf('Failed to insert entity of type \'%s\'', $this->entityName),
217
                $e->getCode(),
218
                $e
219
            );
220
        }
221
    }
222
223
    /**
224
     * @param EntityInterface          $entity
225
     * @param array<string|int, mixed> $options
226
     *
227
     * @return bool
228
     *
229
     * @throws PersistenceException
230
     */
231
    public function delete(EntityInterface $entity, array $options = []): bool
232
    {
233
        $transaction = (bool)($options['transaction'] ?? false);
234
        $flush = (bool)($options['flush'] ?? true);
235
236
        try {
237
            if ($transaction) {
238
                $this->beginTransaction();
239
            }
240
241
            $this->entityManager->remove($entity);
242
243
            if ($flush) {
244
                $this->flush();
245
            }
246
247
            if ($transaction) {
248
                $this->commitTransaction();
249
            }
250
251
            return true;
252
        } catch (PersistenceException $e) {
253
            if ($transaction) {
254
                $this->rollbackTransaction();
255
            }
256
            throw $e;
257
        } catch (\Exception $e) {
258
            if ($transaction) {
259
                $this->rollbackTransaction();
260
            }
261
262
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
263
264
            throw new PersistenceException(
265
                sprintf('Failed to delete entity of type \'%s\'', $this->entityName),
266
                $e->getCode(),
267
                $e
268
            );
269
        }
270
    }
271
272
    /**
273
     * @param iterable<EntityInterface> $collection
274
     * @param array<string|int, mixed>  $options
275
     *
276
     * @return int
277
     *
278
     * @throws PersistenceException
279
     */
280
    public function deleteCollection(iterable $collection, array $options = []): int
281
    {
282
        $transaction = (bool)($options['transaction'] ?? true);
283
        $flush = (bool)($options['flush'] ?? true);
284
285
        try {
286
            if ($transaction) {
287
                $this->beginTransaction();
288
            }
289
290
            $saveOptions = array_replace_recursive(
291
                [
292
                    'flush' => !$flush,
293
                    'transaction' => !$transaction
294
                ],
295
                $options['entity_options'] ?? []
296
            );
297
298
            $deletedCount = 0;
299
            foreach ($collection as $entity) {
300
                if ($this->delete($entity, $saveOptions)) {
301
                    $deletedCount++;
302
                }
303
            }
304
305
            if ($flush) {
306
                $this->flush();
307
            }
308
309
            if ($transaction) {
310
                $this->commitTransaction();
311
            }
312
313
            return $deletedCount;
314
        } catch (\Exception $e) {
315
            if ($transaction) {
316
                $this->beginTransaction();
317
            }
318
319
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
320
321
            throw new PersistenceException(
322
                sprintf('Failed to save collection of type \'%s\'', $this->entityName),
323
                $e->getCode(),
324
                $e
325
            );
326
        }
327
    }
328
329
    /**
330
     * Perform a flush of the unit of work.
331
     *
332
     * @throws PersistenceException
333
     */
334
    public function flush(): void
335
    {
336
        try {
337
            $this->entityManager->flush();
338
        } catch (\Exception $e) {
339
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
340
341
            throw new PersistenceException(
342
                sprintf('Failed to flush entity of type \'%s\'', $this->entityName),
343
                $e->getCode(),
344
                $e
345
            );
346
        }
347
    }
348
349
    /**
350
     * Release managed entities from the identity map.
351
     *
352
     * @return void
353
     *
354
     * @throws PersistenceException
355
     */
356
    public function clear(): void
357
    {
358
        try {
359
            $this->entityManager->clear();
360
        } catch (\Exception $e) {
361
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
362
363
            throw new PersistenceException(
364
                sprintf('The clear  operation failed for entity \'%s\'', $this->entityName),
365
                $e->getCode(),
366
                $e
367
            );
368
        }
369
    }
370
371
    /**
372
     * @param EntityInterface $entity
373
     *
374
     * @throws PersistenceException
375
     */
376
    public function refresh(EntityInterface $entity): void
377
    {
378
        try {
379
            $this->entityManager->refresh($entity);
380
        } catch (\Exception $e) {
381
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
382
383
            throw new PersistenceException(
384
                sprintf('The refresh operation failed for entity \'%s\'', $this->entityName),
385
                $e->getCode(),
386
                $e
387
            );
388
        }
389
    }
390
391
    /**
392
     * @throws PersistenceException
393
     */
394
    public function beginTransaction(): void
395
    {
396
        try {
397
            $this->entityManager->beginTransaction();
398
        } catch (\Exception $e) {
399
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
400
401
            throw new PersistenceException(
402
                sprintf('Failed to start transaction for entity \'%s\'', $this->entityName),
403
                $e->getCode(),
404
                $e
405
            );
406
        }
407
    }
408
409
    /**
410
     * @throws PersistenceException
411
     */
412
    public function commitTransaction(): void
413
    {
414
        try {
415
            $this->entityManager->commit();
416
        } catch (\Exception $e) {
417
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
418
419
            throw new PersistenceException(
420
                sprintf('Failed to commit transaction for entity \'%s\'', $this->entityName),
421
                $e->getCode(),
422
                $e
423
            );
424
        }
425
    }
426
427
    /**
428
     * @throws PersistenceException
429
     */
430
    public function rollbackTransaction(): void
431
    {
432
        try {
433
            $this->entityManager->rollback();
434
        } catch (\Exception $e) {
435
            $this->logger->error($e->getMessage(), ['exception' => $e, 'entity_name' => $this->entityName]);
436
437
            throw new PersistenceException(
438
                sprintf('Failed to rollback transaction for entity \'%s\'', $this->entityName),
439
                $e->getCode(),
440
                $e
441
            );
442
        }
443
    }
444
}
445