PersistentBatchObjectsManager::processChunk()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the sauls/object-registry-bundle package.
4
 *
5
 * @author    Saulius Vaičeliūnas <[email protected]>
6
 * @link      http://saulius.vaiceliunas.lt
7
 * @copyright 2018
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Sauls\Bundle\ObjectRegistryBundle\Manager;
14
15
use Doctrine\ORM\EntityManagerInterface;
16
use Psr\Log\LoggerInterface;
17
use Sauls\Bundle\ObjectRegistryBundle\Batch\Operation\OperationInterface;
18
use Sauls\Bundle\ObjectRegistryBundle\Batch\Operation\PersistOperation;
19
use Sauls\Bundle\ObjectRegistryBundle\Batch\Operation\RemoveOperation;
20
use Sauls\Bundle\ObjectRegistryBundle\Collection\BatchOperationCollectionInterface;
21
use Sauls\Bundle\ObjectRegistryBundle\Event\GenericDoctrineCollectionEvent;
22
use Sauls\Bundle\ObjectRegistryBundle\EventDispatcher\EventDispatcherInterface;
23
use Sauls\Bundle\ObjectRegistryBundle\Exception\EmptyDataException;
24
use Sauls\Bundle\ObjectRegistryBundle\Exception\ManagerNotFoundException;
25
use Sauls\Bundle\ObjectRegistryBundle\Exception\OperationNotFoundException;
26
27
class PersistentBatchObjectsManager implements PersistentBatchObjectsManagerInterface
28
{
29
    private const CLEAR_NONE = 'none';
30
    private const CLEAR_ALL = 'all';
31
    private const CLEAR_OBJECT = 'object';
32
    private const CLEAR_SPECIFIC = 'specific';
33
34
    /**
35
     * @var array
36
     */
37
    private $chunks;
38
39
    /**
40
     * @var DoctrineEntityManagerInterface
41
     */
42
    private $manager;
43
    /**
44
     * @var EntityManagerInterface
45
     */
46
    private $entityManager;
47
    /**
48
     * @var EventDispatcherInterface
49
     */
50
    private $eventDispatcher;
51
    /**
52
     * @var LoggerInterface
53
     */
54
    private $logger;
55
    /**
56
     * @var BatchOperationCollectionInterface
57
     */
58
    private $batchOperations;
59
60
    /**
61
     * @var string
62
     */
63
    private $clearState = self::CLEAR_ALL;
64
65
    /**
66
     * @var array
67
     */
68
    private $clearSpecificObjectNames = [];
69
70 10
    public function __construct(
71
        EntityManagerInterface $entityManager,
72
        EventDispatcherInterface $eventDispatcher,
73
        BatchOperationCollectionInterface $batchOperations,
74
        LoggerInterface $logger
75
    ) {
76 10
        $this->entityManager = $entityManager;
77 10
        $this->eventDispatcher = $eventDispatcher;
78 10
        $this->batchOperations = $batchOperations;
79 10
        $this->logger = $logger;
80 10
    }
81
82 9
    public function save(): bool
83
    {
84 9
        return $this->process(PersistOperation::NAME);
85
    }
86
87 10
    private function process(string $operationName): bool
88
    {
89 10
        $this->checkManagerIsNotNull();
90 9
        $this->checkChunksIsNotEmpty();
91
92
        try {
93 8
            $self = $this;
94
            $this->entityManager->transactional(function () use ($operationName, $self) {
95 7
                $self->processChunks($operationName);
96 8
            });
97 6
            return true;
98 2
        } catch (\Throwable $exception) {
99 2
            $this->logger->critical($exception->getMessage(), [$exception]);
100 2
            return false;
101
        }
102
    }
103
104 10
    private function checkManagerIsNotNull(): void
105
    {
106 10
        if (null === $this->manager) {
107 1
            throw new ManagerNotFoundException(
108 1
                'Manager cannot be `null` maybe you forgot to assign it? To do so use `setManager` method'
109
            );
110
        }
111 9
    }
112
113 9
    private function checkChunksIsNotEmpty(): void
114
    {
115 9
        if (true === empty($this->chunks)) {
116 1
            throw new EmptyDataException(
117 1
                \sprintf('No data to work with maybe you forgot to fill?')
118
            );
119
        }
120 8
    }
121
122 7
    private function processChunks(string $operationName): void
123
    {
124 7
        $operation = $this->getOperationByName($operationName);
125
126 6
        foreach ($this->chunks as $chunk) {
127 6
            $event = new GenericDoctrineCollectionEvent($chunk);
128 6
            $this->eventDispatcher->dispatch($operation->getPreEventName(), $event);
129 6
            $this->processChunk($operation, $chunk);
130 6
            $this->entityManager->flush();
131 6
            $this->eventDispatcher->dispatch($operation->getPostEventName(), $event);
132 6
            $this->clear();
133
        }
134 6
    }
135
136 7
    private function getOperationByName(string $operationName): OperationInterface
137
    {
138 7
        $operation = $this->batchOperations->get($operationName);
139
140 7
        if (null === $operation) {
141 1
            throw new OperationNotFoundException(sprintf('Batch operation `%s` was not found', $operationName));
142
        }
143
144 6
        return $operation;
145
    }
146
147 6
    private function processChunk(OperationInterface $operation, array $chunk): void
148
    {
149 6
        foreach ($chunk as $object) {
150 6
            $this->manager->checkObjectIntegrity($object);
151 6
            $operation->execute($object);
152
153 6
            if ($this->isState(self::CLEAR_OBJECT)) {
154 1
                $this->setSpecificObjectName(\get_class($object));
155
            }
156
        }
157 6
    }
158
159 6
    private function isState(string $state): bool
160
    {
161 6
        return true === ($state === $this->clearState);
162
    }
163
164 2
    private function setSpecificObjectName(string $objectName): void
165
    {
166 2
        if (false === \in_array($objectName, $this->clearSpecificObjectNames)) {
167 2
            $this->clearSpecificObjectNames[] = $objectName;
168
        }
169 2
    }
170
171 1
    private function addSpecificObjectNames(array $objectNames): void
172
    {
173 1
        foreach ($objectNames as $objectName) {
174 1
            $this->setSpecificObjectName($objectName);
175
        }
176 1
    }
177
178 6
    private function clear(): void
179
    {
180 6
        switch ($this->clearState) {
181 6
            case self::CLEAR_ALL:
182 3
                $this->entityManager->clear();
183 3
                break;
184 3
            case self::CLEAR_OBJECT:
185 2
            case self::CLEAR_SPECIFIC:
186 2
                foreach ($this->clearSpecificObjectNames as $objectName) {
187 2
                    $this->entityManager->clear($objectName);
188
                }
189 2
                break;
190 1
            case self::CLEAR_NONE:
191 1
                break;
192
        }
193 6
    }
194
195 1
    public function remove(): bool
196
    {
197 1
        return $this->process(RemoveOperation::NAME);
198
    }
199
200 8
    public function fill(array $objects, int $batchSize): void
201
    {
202 8
        $this->chunks = $this->splitToChunks($objects, $batchSize);
203 8
    }
204
205 8
    private function splitToChunks(array $objects, int $batchSize): array
206
    {
207 8
        return \array_chunk($objects, $batchSize);
208
    }
209
210 9
    public function setManager(DoctrineEntityManagerInterface $manager): void
211
    {
212 9
        $this->manager = $manager;
213 9
    }
214
215 1
    public function setClearAll(): PersistentBatchObjectsManagerInterface
216
    {
217 1
        $this->clearState = self::CLEAR_ALL;
218
219 1
        return $this;
220
    }
221
222 1
    public function setClearSpecific(array $objectNames): PersistentBatchObjectsManagerInterface
223
    {
224 1
        $this->clearState = self::CLEAR_SPECIFIC;
225 1
        $this->addSpecificObjectNames($objectNames);
226
227 1
        return $this;
228
    }
229
230 1
    public function setClearObject(): PersistentBatchObjectsManagerInterface
231
    {
232 1
        $this->clearState = self::CLEAR_OBJECT;
233
234 1
        return $this;
235
    }
236
237 1
    public function setClearNone(): PersistentBatchObjectsManagerInterface
238
    {
239 1
        $this->clearState = self::CLEAR_NONE;
240
241 1
        return $this;
242
    }
243
}
244