Completed
Pull Request — master (#15)
by Jonathan
05:36
created

UnitOfWork   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 336
Duplicated Lines 0 %

Test Coverage

Coverage 96.75%

Importance

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

23 Methods

Rating   Name   Duplication   Size   Complexity  
A propertyChanged() 0 13 3
A createObject() 0 19 2
A isScheduledForRemove() 0 3 1
A getObjectsToUpdate() 0 3 1
A contains() 0 4 2
A getOrCreateObject() 0 9 2
A commit() 0 30 4
A getObjectsToPersist() 0 3 1
A getObjectPersister() 0 4 1
A persist() 0 15 3
A isScheduledForUpdate() 0 3 1
A clear() 0 10 1
A __construct() 0 21 1
A getObjectChangeSet() 0 3 1
A getObjectRepository() 0 4 1
A isScheduledForPersist() 0 3 1
A getObjectsToRemove() 0 3 1
A refresh() 0 3 1
A update() 0 12 2
A merge() 0 3 1
A detach() 0 3 1
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 function array_merge;
19
use function get_class;
20
use function spl_object_hash;
21
22
/**
23
 * Class for managing the persistence of objects.
24
 */
25
class UnitOfWork implements PropertyChangedListener
26
{
27
    /** @var ObjectManagerInterface */
28
    private $objectManager;
29
30
    /** @var ObjectPersisterFactoryInterface */
31
    private $objectPersisterFactory;
32
33
    /** @var ObjectIdentityMap */
34
    private $objectIdentityMap;
35
36
    /** @var EventDispatcher */
37
    private $eventDispatcher;
38
39
    /** @var Persister */
40
    private $persister;
41
42
    /** @var object[] */
43
    private $objectsToPersist = [];
44
45
    /** @var object[] */
46
    private $objectsToUpdate = [];
47
48
    /** @var object[] */
49
    private $objectsToRemove = [];
50
51
    /** @var ChangeSets */
52
    private $objectChangeSets;
53
54 22
    public function __construct(
55
        ObjectManagerInterface $objectManager,
56
        ObjectPersisterFactoryInterface $objectPersisterFactory,
57
        ObjectIdentityMap $objectIdentityMap,
58
        EventManager $eventManager
59
    ) {
60 22
        $this->objectManager          = $objectManager;
61 22
        $this->objectPersisterFactory = $objectPersisterFactory;
62 22
        $this->objectIdentityMap      = $objectIdentityMap;
63
64 22
        $this->eventDispatcher = new EventDispatcher(
65 22
            $objectManager,
66 22
            $eventManager
67
        );
68 22
        $this->persister       = new Persister(
69 22
            $this,
70 22
            $this->eventDispatcher,
71 22
            $this->objectIdentityMap
72
        );
73
74 22
        $this->objectChangeSets = new ChangeSets();
75 22
    }
76
77
    /**
78
     * @param object $object
79
     */
80 1
    public function merge($object) : void
81
    {
82 1
        $this->getObjectRepository($object)->merge($object);
83 1
    }
84
85
    /**
86
     * @param object $object
87
     */
88 10
    public function persist($object) : void
89
    {
90 10
        if ($this->isScheduledForPersist($object)) {
91
            throw new \InvalidArgumentException('Object is already scheduled for persist.');
92
        }
93
94 10
        $this->eventDispatcher->dispatchPrePersist($object);
95
96 10
        $this->objectsToPersist[spl_object_hash($object)] = $object;
97
98 10
        if (! ($object instanceof NotifyPropertyChanged)) {
99
            return;
100
        }
101
102 10
        $object->addPropertyChangedListener($this);
103 10
    }
104
105
    /**
106
     * @param object $object The instance to update
107
     */
108 10
    public function update($object) : void
109
    {
110 10
        if ($this->isScheduledForUpdate($object)) {
111
            throw new \InvalidArgumentException('Object is already scheduled for update.');
112
        }
113
114 10
        $this->eventDispatcher->dispatchPreUpdate(
115 10
            $object,
116 10
            $this->getObjectChangeSet($object)
117
        );
118
119 10
        $this->objectsToUpdate[spl_object_hash($object)] = $object;
120 10
    }
121
122
    /**
123
     * @param object $object The object instance to remove.
124
     */
125 3
    public function remove($object) : void
126
    {
127 3
        if ($this->isScheduledForRemove($object)) {
128
            throw new \InvalidArgumentException('Object is already scheduled for remove.');
129
        }
130
131 3
        $this->eventDispatcher->dispatchPreRemove($object);
132
133 3
        $this->objectsToRemove[spl_object_hash($object)] = $object;
134 3
    }
135
136 9
    public function clear(?string $objectName = null) : void
137
    {
138 9
        $this->objectIdentityMap->clear($objectName);
139
140 9
        $this->objectsToPersist = [];
141 9
        $this->objectsToUpdate  = [];
142 9
        $this->objectsToRemove  = [];
143 9
        $this->objectChangeSets = new ChangeSets();
144
145 9
        $this->eventDispatcher->dispatchOnClearEvent($objectName);
146 9
    }
147
148
    /**
149
     * @param object $object
150
     */
151 1
    public function detach($object) : void
152
    {
153 1
        $this->objectIdentityMap->detach($object);
154 1
    }
155
156
    /**
157
     * @param object $object
158
     */
159 1
    public function refresh($object) : void
160
    {
161 1
        $this->getObjectRepository($object)->refresh($object);
162 1
    }
163
164
    /**
165
     * @param object $object
166
     */
167 1
    public function contains($object) : bool
168
    {
169 1
        return $this->objectIdentityMap->contains($object)
170 1
            || $this->isScheduledForPersist($object);
171
    }
172
173
    /**
174
     * Commit the contents of the unit of work.
175
     */
176 13
    public function commit() : void
177
    {
178 13
        $this->eventDispatcher->dispatchPreFlush();
179
180 13
        if ($this->objectsToPersist === [] &&
181 13
            $this->objectsToUpdate === [] &&
182 13
            $this->objectsToRemove === []
183
        ) {
184 2
            return; // Nothing to do.
185
        }
186
187 12
        $objects = array_merge(
188 12
            $this->objectsToPersist,
189 12
            $this->objectsToUpdate,
190 12
            $this->objectsToRemove
191
        );
192 12
        $this->eventDispatcher->dispatchPreFlushLifecycleCallbacks($objects);
193
194 12
        $this->eventDispatcher->dispatchOnFlush();
195
196 12
        $this->persister->executePersists();
197 12
        $this->persister->executeUpdates();
198 12
        $this->persister->executeRemoves();
199
200 12
        $this->eventDispatcher->dispatchPostFlush();
201
202 12
        $this->objectsToPersist = [];
203 12
        $this->objectsToUpdate  = [];
204 12
        $this->objectsToRemove  = [];
205 12
        $this->objectChangeSets = new ChangeSets();
206 12
    }
207
208
    /**
209
     * @param object $object
210
     *
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