Failed Conditions
Pull Request — master (#28)
by Jonathan
02:17
created

UnitOfWork::clear()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

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