Failed Conditions
Pull Request — master (#28)
by Jonathan
03:45 queued 16s
created

UnitOfWork::update()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0116

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
crap 2.0116
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\SkeletonMapper;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\Persistence\NotifyPropertyChanged;
9
use Doctrine\Persistence\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 1
    public function merge(object $object) : object
79
    {
80 1
        return $this->getObjectRepository($object)->merge($object);
81
    }
82
83 10
    public function persist(object $object) : void
84
    {
85 10
        if ($this->isScheduledForPersist($object)) {
86
            throw new InvalidArgumentException('Object is already scheduled for persist.');
87
        }
88
89 10
        $this->eventDispatcher->dispatchPrePersist($object);
90
91 10
        $this->objectsToPersist[spl_object_hash($object)] = $object;
92
93 10
        if (! ($object instanceof NotifyPropertyChanged)) {
94
            return;
95
        }
96
97 10
        $object->addPropertyChangedListener($this);
98 10
    }
99
100
    /**
101
     * @param object $object The instance to update
102
     */
103 10
    public function update($object) : void
104
    {
105 10
        if ($this->isScheduledForUpdate($object)) {
106
            throw new InvalidArgumentException('Object is already scheduled for update.');
107
        }
108
109 10
        $this->eventDispatcher->dispatchPreUpdate(
110 10
            $object,
111 10
            $this->getObjectChangeSet($object)
112
        );
113
114 10
        $this->objectsToUpdate[spl_object_hash($object)] = $object;
115 10
    }
116
117
    /**
118
     * @param object $object The object instance to remove.
119
     */
120 3
    public function remove($object) : void
121
    {
122 3
        if ($this->isScheduledForRemove($object)) {
123
            throw new InvalidArgumentException('Object is already scheduled for remove.');
124
        }
125
126 3
        $this->eventDispatcher->dispatchPreRemove($object);
127
128 3
        $this->objectsToRemove[spl_object_hash($object)] = $object;
129 3
    }
130
131 9
    public function clear(?string $objectName = null) : void
132
    {
133 9
        $this->objectIdentityMap->clear($objectName);
134
135 9
        $this->objectsToPersist = [];
136 9
        $this->objectsToUpdate  = [];
137 9
        $this->objectsToRemove  = [];
138 9
        $this->objectChangeSets = new ChangeSets();
139
140 9
        $this->eventDispatcher->dispatchOnClearEvent($objectName);
141 9
    }
142
143
    /**
144
     * @param object $object
145
     */
146 1
    public function detach($object) : void
147
    {
148 1
        $this->objectIdentityMap->detach($object);
149 1
    }
150
151
    /**
152
     * @param object $object
153
     */
154 1
    public function refresh($object) : void
155
    {
156 1
        $this->getObjectRepository($object)->refresh($object);
157 1
    }
158
159
    /**
160
     * @param object $object
161
     */
162 1
    public function contains($object) : bool
163
    {
164 1
        return $this->objectIdentityMap->contains($object)
165 1
            || $this->isScheduledForPersist($object);
166
    }
167
168
    /**
169
     * Commit the contents of the unit of work.
170
     */
171 13
    public function commit() : void
172
    {
173 13
        $this->eventDispatcher->dispatchPreFlush();
174
175 13
        if ($this->objectsToPersist === [] &&
176 13
            $this->objectsToUpdate === [] &&
177 13
            $this->objectsToRemove === []
178
        ) {
179 2
            return; // Nothing to do.
180
        }
181
182 12
        $objects = array_merge(
183 12
            $this->objectsToPersist,
184 12
            $this->objectsToUpdate,
185 12
            $this->objectsToRemove
186
        );
187 12
        $this->eventDispatcher->dispatchPreFlushLifecycleCallbacks($objects);
188
189 12
        $this->eventDispatcher->dispatchOnFlush();
190
191 12
        $this->persister->executePersists();
192 12
        $this->persister->executeUpdates();
193 12
        $this->persister->executeRemoves();
194
195 12
        $this->eventDispatcher->dispatchPostFlush();
196
197 12
        $this->objectsToPersist = [];
198 12
        $this->objectsToUpdate  = [];
199 12
        $this->objectsToRemove  = [];
200 12
        $this->objectChangeSets = new ChangeSets();
201 12
    }
202
203
    /**
204
     * @param object $object
205
     */
206 10
    public function isScheduledForPersist($object) : bool
207
    {
208 10
        return isset($this->objectsToPersist[spl_object_hash($object)]);
209
    }
210
211
    /**
212
     * @return object[]
213
     */
214 12
    public function getObjectsToPersist() : array
215
    {
216 12
        return $this->objectsToPersist;
217
    }
218
219
    /**
220
     * @param object $object
221
     */
222 10
    public function isScheduledForUpdate($object) : bool
223
    {
224 10
        return isset($this->objectsToUpdate[spl_object_hash($object)]);
225
    }
226
227
    /**
228
     * @return object[]
229
     */
230 12
    public function getObjectsToUpdate() : array
231
    {
232 12
        return $this->objectsToUpdate;
233
    }
234
235
    /**
236
     * @param object $object
237
     */
238 3
    public function isScheduledForRemove($object) : bool
239
    {
240 3
        return isset($this->objectsToRemove[spl_object_hash($object)]);
241
    }
242
243
    /**
244
     * @return object[]
245
     */
246 12
    public function getObjectsToRemove() : array
247
    {
248 12
        return $this->objectsToRemove;
249
    }
250
251
    /* PropertyChangedListener implementation */
252
253
    /**
254
     * Notifies this UnitOfWork of a property change in an object.
255
     *
256
     * @param object $object       The entity that owns the property.
257
     * @param string $propertyName The name of the property that changed.
258
     * @param mixed  $oldValue     The old value of the property.
259
     * @param mixed  $newValue     The new value of the property.
260
     */
261 10
    public function propertyChanged($object, $propertyName, $oldValue, $newValue) : void
262
    {
263 10
        if (! $this->isInIdentityMap($object)) {
264 1
            return;
265
        }
266
267 10
        if (! $this->isScheduledForUpdate($object)) {
268 10
            $this->update($object);
269
        }
270
271 10
        $this->objectChangeSets->addObjectChange(
272 10
            $object,
273 10
            new Change($propertyName, $oldValue, $newValue)
274
        );
275 10
    }
276
277
    /**
278
     * Gets the changeset for a object.
279
     *
280
     * @param object $object
281
     */
282 10
    public function getObjectChangeSet($object) : ChangeSet
283
    {
284 10
        return $this->objectChangeSets->getObjectChangeSet($object);
285
    }
286
287
    /**
288
     * Checks whether an object is registered in the identity map of this UnitOfWork.
289
     *
290
     * @param object $object
291
     */
292 10
    public function isInIdentityMap($object) : bool
293
    {
294 10
        return $this->objectIdentityMap->contains($object);
295
    }
296
297
    /**
298
     * @param mixed[] $data
299
     *
300
     * @return object
301
     */
302 18
    public function getOrCreateObject(string $className, array $data)
303
    {
304 18
        $object = $this->objectIdentityMap->tryGetById($className, $data);
305
306 18
        if ($object !== null) {
307 8
            return $object;
308
        }
309
310 17
        return $this->createObject($className, $data);
311
    }
312
313
    /**
314
     * @param object $object
315
     */
316 12
    public function getObjectPersister($object) : ObjectPersisterInterface
317
    {
318 12
        return $this->objectPersisterFactory
319 12
            ->getPersister(get_class($object));
320
    }
321
322
    /**
323
     * @param object $object
324
     */
325 11
    public function getObjectRepository($object) : ObjectRepositoryInterface
326
    {
327 11
        return $this->objectManager
1 ignored issue
show
Bug Best Practice introduced by
The expression return $this->objectMana...ory(get_class($object)) returns the type Doctrine\Persistence\ObjectRepository which includes types incompatible with the type-hinted return Doctrine\SkeletonMapper\...jectRepositoryInterface.
Loading history...
328 11
            ->getRepository(get_class($object));
329
    }
330
331
    /**
332
     * @param mixed[] $data
333
     *
334
     * @return object
335
     */
336 17
    private function createObject(string $className, array $data)
337
    {
338 17
        $repository = $this->objectManager->getRepository($className);
339
340 17
        $object = $repository->create($className);
341
342 17
        if ($object instanceof NotifyPropertyChanged) {
343 17
            $object->addPropertyChangedListener($this);
344
        }
345
346 17
        $this->eventDispatcher->dispatchPreLoad($object, $data);
347
348 17
        $repository->hydrate($object, $data);
349
350 17
        $this->eventDispatcher->dispatchPostLoad($object);
351
352 17
        $this->objectIdentityMap->addToIdentityMap($object, $data);
353
354 17
        return $object;
355
    }
356
}
357