Completed
Push — master ( b277f9...6dfd8b )
by Saulius
09:45 queued 07:30
created

PersistentBatchObjectsManager::process()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 2
rs 9.9332
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
use function Sauls\Component\Helper\get_object_property_value;
27
use function Sauls\Component\Helper\set_object_property_value;
28
29
class PersistentBatchObjectsManager implements PersistentBatchObjectsManagerInterface
30
{
31
    /**
32
     * @var array
33
     */
34
    private $chunks;
35
36
    /**
37
     * @var DoctrineEntityManagerInterface
38
     */
39
    private $manager;
40
    /**
41
     * @var EntityManagerInterface
42
     */
43
    private $entityManager;
44
    /**
45
     * @var EventDispatcherInterface
46
     */
47
    private $eventDispatcher;
48
    /**
49
     * @var LoggerInterface
50
     */
51
    private $logger;
52
    /**
53
     * @var BatchOperationCollectionInterface
54
     */
55
    private $batchOperations;
56
57
    /**
58
     * @var array
59
     */
60
    private $refreshProperties;
61
62 7
    public function __construct(
63
        EntityManagerInterface $entityManager,
64
        EventDispatcherInterface $eventDispatcher,
65
        BatchOperationCollectionInterface $batchOperations,
66
        LoggerInterface $logger
67
    ) {
68 7
        $this->entityManager = $entityManager;
69 7
        $this->eventDispatcher = $eventDispatcher;
70 7
        $this->batchOperations = $batchOperations;
71 7
        $this->logger = $logger;
72 7
    }
73
74 6
    public function save(): bool
75
    {
76 6
        return $this->process(PersistOperation::NAME);
77
    }
78
79 7
    private function process(string $operationName): bool
80
    {
81 7
        $this->checkManagerIsNotNull();
82 6
        $this->checkChunksIsNotEmpty();
83
84
        try {
85 5
            $self = $this;
86
            $this->entityManager->transactional(function () use ($operationName, $self) {
87 4
                $self->processChunks($operationName);
88 5
            });
89 3
            return true;
90 2
        } catch (\Throwable $exception) {
91 2
            $this->logger->critical($exception->getMessage(), [$exception]);
92 2
            return false;
93
        }
94
    }
95
96 7
    private function checkManagerIsNotNull(): void
97
    {
98 7
        if (null === $this->manager) {
99 1
            throw new ManagerNotFoundException(
100 1
                'Manager cannot be `null` maybe you forgot to assign it? To do so use `setManager` method'
101
            );
102
        }
103 6
    }
104
105 6
    private function checkChunksIsNotEmpty(): void
106
    {
107 6
        if (true === empty($this->chunks)) {
108 1
            throw new EmptyDataException(
109 1
                \sprintf('No data to work with maybe you forgot to fill?')
110
            );
111
        }
112 5
    }
113
114 4
    private function processChunks(string $operationName): void
115
    {
116 4
        $operation = $this->getOperationByName($operationName);
117
118 3
        foreach ($this->chunks as $chunk) {
119 3
            $event = new GenericDoctrineCollectionEvent($chunk);
120 3
            $this->eventDispatcher->dispatch($operation->getPreEventName(), $event);
121 3
            $this->processChunk($operation, $chunk);
122 3
            $this->entityManager->flush();
123 3
            $this->eventDispatcher->dispatch($operation->getPostEventName(), $event);
124 3
            $this->entityManager->clear();
125
        }
126 3
    }
127
128 4
    private function getOperationByName(string $operationName): OperationInterface
129
    {
130 4
        $operation = $this->batchOperations->get($operationName);
131
132 4
        if (null === $operation) {
133 1
            throw new OperationNotFoundException(sprintf('Batch operation `%s` was not found', $operationName));
134
        }
135
136 3
        return $operation;
137
    }
138
139 3
    private function processChunk(OperationInterface $operation, array $chunk): void
140
    {
141 3
        foreach ($chunk as $object) {
142 3
            $this->manager->checkObjectIntegrity($object);
143 3
            $this->refresh($object);
144 3
            $operation->execute($object);
145
        }
146 3
    }
147
148 1
    public function remove(): bool
149
    {
150 1
        return $this->process(RemoveOperation::NAME);
151
    }
152
153 5
    public function fill(array $objects, int $batchSize): void
154
    {
155 5
        $this->chunks = $this->splitToChunks($objects, $batchSize);
156 5
    }
157
158 5
    private function splitToChunks(array $objects, int $batchSize): array
159
    {
160 5
        return \array_chunk($objects, $batchSize);
161
    }
162
163 6
    public function setManager(DoctrineEntityManagerInterface $manager): void
164
    {
165 6
        $this->manager = $manager;
166 6
    }
167
168 1
    public function setRefresh(array $properties): void
169
    {
170 1
        $this->refreshProperties = $properties;
171 1
    }
172
173
    /**
174
     * @param $object
175
     * @throws \Sauls\Component\Helper\Exception\PropertyNotAccessibleException
176
     * @throws \Sauls\Component\Helper\Exception\ClassPropertyNotSetException
177
     */
178 3
    private function refresh(object $object): void
179
    {
180 3
        if (empty($this->refreshProperties)) {
181 2
            return;
182
        }
183
184 1
        foreach ($this->refreshProperties as $property) {
185 1
            $value = get_object_property_value($object, $property);
186 1
            if (false === \is_object($value)) {
187 1
                continue;
188
            }
189
190 1
            $metadata = $this->entityManager->getClassMetadata(\get_class($value));
191 1
            $freshValue = $this->entityManager->find($metadata->getName(), $metadata->getIdentifierValues($value));
192
193 1
            set_object_property_value($object, $property, $freshValue);
194
        }
195 1
    }
196
}
197