UnitOfWork   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 335
Duplicated Lines 0 %

Test Coverage

Coverage 96.75%

Importance

Changes 9
Bugs 0 Features 0
Metric Value
eloc 99
c 9
b 0
f 0
dl 0
loc 335
ccs 119
cts 123
cp 0.9675
rs 9.6
wmc 35

23 Methods

Rating   Name   Duplication   Size   Complexity  
A isScheduledForRemove() 0 3 1
A getObjectsToUpdate() 0 3 1
A getObjectsToPersist() 0 3 1
A isScheduledForUpdate() 0 3 1
A getObjectRepository() 0 4 1
A isScheduledForPersist() 0 3 1
A getObjectsToRemove() 0 3 1
A __construct() 0 21 1
A merge() 0 3 1
A detach() 0 3 1
A propertyChanged() 0 13 3
A createObject() 0 19 2
A contains() 0 4 2
A getOrCreateObject() 0 9 2
A commit() 0 30 4
A getObjectPersister() 0 4 1
A persist() 0 15 3
A clear() 0 10 1
A getObjectChangeSet() 0 3 1
A refresh() 0 3 1
A update() 0 12 2
A remove() 0 9 2
A isInIdentityMap() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\SkeletonMapper;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Common\NotifyPropertyChanged;
9
use Doctrine\Common\PropertyChangedListener;
10
use Doctrine\SkeletonMapper\ObjectRepository\ObjectRepositoryInterface;
11
use Doctrine\SkeletonMapper\Persister\ObjectPersisterFactoryInterface;
12
use Doctrine\SkeletonMapper\Persister\ObjectPersisterInterface;
13
use Doctrine\SkeletonMapper\UnitOfWork\Change;
14
use Doctrine\SkeletonMapper\UnitOfWork\ChangeSet;
15
use Doctrine\SkeletonMapper\UnitOfWork\ChangeSets;
16
use Doctrine\SkeletonMapper\UnitOfWork\EventDispatcher;
17
use Doctrine\SkeletonMapper\UnitOfWork\Persister;
18
use InvalidArgumentException;
19
use function array_merge;
20
use function get_class;
21
use function spl_object_hash;
22
23
/**
24
 * Class for managing the persistence of objects.
25
 */
26
class UnitOfWork implements PropertyChangedListener
27
{
28
    /** @var ObjectManagerInterface */
29
    private $objectManager;
30
31
    /** @var ObjectPersisterFactoryInterface */
32
    private $objectPersisterFactory;
33
34
    /** @var ObjectIdentityMap */
35
    private $objectIdentityMap;
36
37
    /** @var EventDispatcher */
38
    private $eventDispatcher;
39
40
    /** @var Persister */
41
    private $persister;
42
43
    /** @var object[] */
44
    private $objectsToPersist = [];
45
46
    /** @var object[] */
47
    private $objectsToUpdate = [];
48
49
    /** @var object[] */
50
    private $objectsToRemove = [];
51
52
    /** @var ChangeSets */
53
    private $objectChangeSets;
54
55 22
    public function __construct(
56
        ObjectManagerInterface $objectManager,
57
        ObjectPersisterFactoryInterface $objectPersisterFactory,
58
        ObjectIdentityMap $objectIdentityMap,
59
        EventManager $eventManager
60
    ) {
61 22
        $this->objectManager          = $objectManager;
62 22
        $this->objectPersisterFactory = $objectPersisterFactory;
63 22
        $this->objectIdentityMap      = $objectIdentityMap;
64
65 22
        $this->eventDispatcher = new EventDispatcher(
66 22
            $objectManager,
67 22
            $eventManager
68
        );
69 22
        $this->persister       = new Persister(
70 22
            $this,
71 22
            $this->eventDispatcher,
72 22
            $this->objectIdentityMap
73
        );
74
75 22
        $this->objectChangeSets = new ChangeSets();
76 22
    }
77
78
    /**
79
     * @param object $object
80
     */
81 1
    public function merge($object) : void
82
    {
83 1
        $this->getObjectRepository($object)->merge($object);
84 1
    }
85
86
    /**
87
     * @param object $object
88
     */
89 10
    public function persist($object) : void
90
    {
91 10
        if ($this->isScheduledForPersist($object)) {
92
            throw new InvalidArgumentException('Object is already scheduled for persist.');
93
        }
94
95 10
        $this->eventDispatcher->dispatchPrePersist($object);
96
97 10
        $this->objectsToPersist[spl_object_hash($object)] = $object;
98
99 10
        if (! ($object instanceof NotifyPropertyChanged)) {
100
            return;
101
        }
102
103 10
        $object->addPropertyChangedListener($this);
104 10
    }
105
106
    /**
107
     * @param object $object The instance to update
108
     */
109 10
    public function update($object) : void
110
    {
111 10
        if ($this->isScheduledForUpdate($object)) {
112
            throw new InvalidArgumentException('Object is already scheduled for update.');
113
        }
114
115 10
        $this->eventDispatcher->dispatchPreUpdate(
116 10
            $object,
117 10
            $this->getObjectChangeSet($object)
118
        );
119
120 10
        $this->objectsToUpdate[spl_object_hash($object)] = $object;
121 10
    }
122
123
    /**
124
     * @param object $object The object instance to remove.
125
     */
126 3
    public function remove($object) : void
127
    {
128 3
        if ($this->isScheduledForRemove($object)) {
129
            throw new InvalidArgumentException('Object is already scheduled for remove.');
130
        }
131
132 3
        $this->eventDispatcher->dispatchPreRemove($object);
133
134 3
        $this->objectsToRemove[spl_object_hash($object)] = $object;
135 3
    }
136
137 9
    public function clear(?string $objectName = null) : void
138
    {
139 9
        $this->objectIdentityMap->clear($objectName);
140
141 9
        $this->objectsToPersist = [];
142 9
        $this->objectsToUpdate  = [];
143 9
        $this->objectsToRemove  = [];
144 9
        $this->objectChangeSets = new ChangeSets();
145
146 9
        $this->eventDispatcher->dispatchOnClearEvent($objectName);
147 9
    }
148
149
    /**
150
     * @param object $object
151
     */
152 1
    public function detach($object) : void
153
    {
154 1
        $this->objectIdentityMap->detach($object);
155 1
    }
156
157
    /**
158
     * @param object $object
159
     */
160 1
    public function refresh($object) : void
161
    {
162 1
        $this->getObjectRepository($object)->refresh($object);
163 1
    }
164
165
    /**
166
     * @param object $object
167
     */
168 1
    public function contains($object) : bool
169
    {
170 1
        return $this->objectIdentityMap->contains($object)
171 1
            || $this->isScheduledForPersist($object);
172
    }
173
174
    /**
175
     * Commit the contents of the unit of work.
176
     */
177 13
    public function commit() : void
178
    {
179 13
        $this->eventDispatcher->dispatchPreFlush();
180
181 13
        if ($this->objectsToPersist === [] &&
182 13
            $this->objectsToUpdate === [] &&
183 13
            $this->objectsToRemove === []
184
        ) {
185 2
            return; // Nothing to do.
186
        }
187
188 12
        $objects = array_merge(
189 12
            $this->objectsToPersist,
190 12
            $this->objectsToUpdate,
191 12
            $this->objectsToRemove
192
        );
193 12
        $this->eventDispatcher->dispatchPreFlushLifecycleCallbacks($objects);
194
195 12
        $this->eventDispatcher->dispatchOnFlush();
196
197 12
        $this->persister->executePersists();
198 12
        $this->persister->executeUpdates();
199 12
        $this->persister->executeRemoves();
200
201 12
        $this->eventDispatcher->dispatchPostFlush();
202
203 12
        $this->objectsToPersist = [];
204 12
        $this->objectsToUpdate  = [];
205 12
        $this->objectsToRemove  = [];
206 12
        $this->objectChangeSets = new ChangeSets();
207 12
    }
208
209
    /**
210
     * @param object $object
211
     */
212 10
    public function isScheduledForPersist($object) : bool
213
    {
214 10
        return isset($this->objectsToPersist[spl_object_hash($object)]);
215
    }
216
217
    /**
218
     * @return object[]
219
     */
220 12
    public function getObjectsToPersist() : array
221
    {
222 12
        return $this->objectsToPersist;
223
    }
224
225
    /**
226
     * @param object $object
227
     */
228 10
    public function isScheduledForUpdate($object) : bool
229
    {
230 10
        return isset($this->objectsToUpdate[spl_object_hash($object)]);
231
    }
232
233
    /**
234
     * @return object[]
235
     */
236 12
    public function getObjectsToUpdate() : array
237
    {
238 12
        return $this->objectsToUpdate;
239
    }
240
241
    /**
242
     * @param object $object
243
     */
244 3
    public function isScheduledForRemove($object) : bool
245
    {
246 3
        return isset($this->objectsToRemove[spl_object_hash($object)]);
247
    }
248
249
    /**
250
     * @return object[]
251
     */
252 12
    public function getObjectsToRemove() : array
253
    {
254 12
        return $this->objectsToRemove;
255
    }
256
257
    /* PropertyChangedListener implementation */
258
259
    /**
260
     * Notifies this UnitOfWork of a property change in an object.
261
     *
262
     * @param object $object       The entity that owns the property.
263
     * @param string $propertyName The name of the property that changed.
264
     * @param mixed  $oldValue     The old value of the property.
265
     * @param mixed  $newValue     The new value of the property.
266
     */
267 10
    public function propertyChanged($object, $propertyName, $oldValue, $newValue) : void
268
    {
269 10
        if (! $this->isInIdentityMap($object)) {
270 1
            return;
271
        }
272
273 10
        if (! $this->isScheduledForUpdate($object)) {
274 10
            $this->update($object);
275
        }
276
277 10
        $this->objectChangeSets->addObjectChange(
278 10
            $object,
279 10
            new Change($propertyName, $oldValue, $newValue)
280
        );
281 10
    }
282
283
    /**
284
     * Gets the changeset for a object.
285
     *
286
     * @param object $object
287
     */
288 10
    public function getObjectChangeSet($object) : ChangeSet
289
    {
290 10
        return $this->objectChangeSets->getObjectChangeSet($object);
291
    }
292
293
    /**
294
     * Checks whether an object is registered in the identity map of this UnitOfWork.
295
     *
296
     * @param object $object
297
     */
298 10
    public function isInIdentityMap($object) : bool
299
    {
300 10
        return $this->objectIdentityMap->contains($object);
301
    }
302
303
    /**
304
     * @param mixed[] $data
305
     *
306
     * @return object
307
     */
308 18
    public function getOrCreateObject(string $className, array $data)
309
    {
310 18
        $object = $this->objectIdentityMap->tryGetById($className, $data);
311
312 18
        if ($object !== null) {
313 8
            return $object;
314
        }
315
316 17
        return $this->createObject($className, $data);
317
    }
318
319
    /**
320
     * @param object $object
321
     */
322 12
    public function getObjectPersister($object) : ObjectPersisterInterface
323
    {
324 12
        return $this->objectPersisterFactory
325 12
            ->getPersister(get_class($object));
326
    }
327
328
    /**
329
     * @param object $object
330
     */
331 11
    public function getObjectRepository($object) : ObjectRepositoryInterface
332
    {
333 11
        return $this->objectManager
334 11
            ->getRepository(get_class($object));
335
    }
336
337
    /**
338
     * @param mixed[] $data
339
     *
340
     * @return object
341
     */
342 17
    private function createObject(string $className, array $data)
343
    {
344 17
        $repository = $this->objectManager->getRepository($className);
345
346 17
        $object = $repository->create($className);
347
348 17
        if ($object instanceof NotifyPropertyChanged) {
349 17
            $object->addPropertyChangedListener($this);
350
        }
351
352 17
        $this->eventDispatcher->dispatchPreLoad($object, $data);
353
354 17
        $repository->hydrate($object, $data);
355
356 17
        $this->eventDispatcher->dispatchPostLoad($object);
357
358 17
        $this->objectIdentityMap->addToIdentityMap($object, $data);
359
360 17
        return $object;
361
    }
362
}
363