1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Kunstmaan\AdminListBundle\Service; |
4
|
|
|
|
5
|
|
|
use Doctrine\Common\Persistence\ObjectManager; |
6
|
|
|
use FOS\UserBundle\Model\User; |
7
|
|
|
use Kunstmaan\AdminBundle\Entity\UserInterface; |
8
|
|
|
use Kunstmaan\AdminListBundle\Entity\LockableEntity; |
9
|
|
|
use Kunstmaan\AdminListBundle\Entity\EntityVersionLock; |
10
|
|
|
use Kunstmaan\AdminListBundle\Entity\LockableEntityInterface; |
11
|
|
|
use Kunstmaan\AdminListBundle\Repository\EntityVersionLockRepository; |
12
|
|
|
|
13
|
|
|
class EntityVersionLockService |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var ObjectManager |
17
|
|
|
*/ |
18
|
|
|
private $objectManager; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var int |
22
|
|
|
*/ |
23
|
|
|
private $threshold; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var bool |
27
|
|
|
*/ |
28
|
|
|
private $lockEnabled; |
29
|
|
|
|
30
|
6 |
|
public function __construct(ObjectManager $em, $threshold, $lockEnabled) |
31
|
|
|
{ |
32
|
6 |
|
$this->setObjectManager($em); |
33
|
6 |
|
$this->setThreshold($threshold); |
34
|
6 |
|
$this->setLockEnabled($lockEnabled); |
35
|
6 |
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @param LockableEntityInterface $entity |
39
|
|
|
* |
40
|
|
|
* @return bool |
41
|
|
|
*/ |
42
|
3 |
|
public function isEntityBelowThreshold(LockableEntityInterface $entity) |
43
|
|
|
{ |
44
|
|
|
/** @var LockableEntity $lockable */ |
45
|
3 |
|
$lockable = $this->getLockableEntity($entity, false); |
46
|
|
|
|
47
|
3 |
|
if ($this->lockEnabled && $lockable->getId() !== null) { |
48
|
2 |
|
$now = new \DateTime(); |
49
|
2 |
|
$thresholdDate = clone $lockable->getUpdated(); |
50
|
2 |
|
$thresholdDate->add(new \DateInterval('PT'.$this->threshold.'S')); |
51
|
|
|
|
52
|
2 |
|
return $thresholdDate > $now; |
53
|
|
|
} |
54
|
|
|
|
55
|
1 |
|
return false; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @param User $user |
60
|
|
|
* @param LockableEntityInterface $entity |
61
|
|
|
* |
62
|
|
|
* @return bool |
63
|
|
|
*/ |
64
|
2 |
|
public function isEntityLocked(/*\Kunstmaan\AdminBundle\Entity\UserInterface*/ $user, LockableEntityInterface $entity) |
65
|
|
|
{ |
66
|
|
|
// NEXT_MAJOR: remove type check and enable parameter typehint |
67
|
2 |
View Code Duplication |
if (!$user instanceof User && !$user instanceof UserInterface) { |
|
|
|
|
68
|
|
|
throw new \InvalidArgumentException(sprintf('The "$user" argument must be of type "%s" or implement the "%s" interface', User::class, UserInterface::class)); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** @var LockableEntity $lockable */ |
72
|
2 |
|
$lockable = $this->getLockableEntity($entity); |
73
|
|
|
|
74
|
2 |
|
if ($this->lockEnabled) { |
75
|
2 |
|
$this->removeExpiredLocks($lockable); |
76
|
2 |
|
$locks = $this->getEntityVersionLocksByLockableEntity($lockable, $user); |
77
|
|
|
|
78
|
|
|
if ($locks === null || !\count($locks)) { |
79
|
|
|
$this->createEntityVersionLock($user, $lockable); |
80
|
|
|
|
81
|
|
|
$lockable->setUpdated(new \DateTime()); |
82
|
|
|
$this->objectManager->flush(); |
83
|
|
|
|
84
|
|
|
return false; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
return true; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
return false; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* When editing the entity, create a new entity translation lock. |
95
|
|
|
* |
96
|
|
|
* @param User $user |
97
|
|
|
* @param LockableEntity $entity |
98
|
|
|
*/ |
99
|
|
|
protected function createEntityVersionLock(/*\Kunstmaan\AdminBundle\Entity\UserInterface*/ $user, LockableEntity $entity) |
100
|
|
|
{ |
101
|
|
|
// NEXT_MAJOR: remove type check and enable parameter typehint |
102
|
|
View Code Duplication |
if (!$user instanceof User && !$user instanceof UserInterface) { |
|
|
|
|
103
|
|
|
throw new \InvalidArgumentException(sprintf('The "$user" argument must be of type "%s" or implement the "%s" interface', User::class, UserInterface::class)); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** @var EntityVersionLock $lock */ |
107
|
|
|
$lock = $this->objectManager->getRepository(EntityVersionLock::class)->findOneBy([ |
108
|
|
|
'owner' => $user->getUsername(), |
109
|
|
|
'lockableEntity' => $entity, |
110
|
|
|
]); |
111
|
|
|
if (!$lock) { |
112
|
|
|
$lock = new EntityVersionLock(); |
113
|
|
|
} |
114
|
|
|
$lock->setOwner($user->getUsername()); |
115
|
|
|
$lock->setLockableEntity($entity); |
116
|
|
|
$lock->setCreatedAt(new \DateTime()); |
117
|
|
|
$this->objectManager->persist($lock); |
118
|
|
|
$this->objectManager->flush(); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param LockableEntityInterface $entity |
123
|
|
|
* @param User $userToExclude |
|
|
|
|
124
|
|
|
* |
125
|
|
|
* @return array |
126
|
|
|
*/ |
127
|
1 |
|
public function getUsersWithEntityVersionLock(LockableEntityInterface $entity, /*\Kunstmaan\AdminBundle\Entity\UserInterface*/ $userToExclude = null) |
128
|
|
|
{ |
129
|
|
|
// NEXT_MAJOR: remove type check and enable parameter typehint |
130
|
1 |
View Code Duplication |
if (!$userToExclude instanceof User && !$userToExclude instanceof UserInterface) { |
|
|
|
|
131
|
|
|
throw new \InvalidArgumentException(sprintf('The "$userToExclude" argument must be of type "%s" or implement the "%s" interface', User::class, UserInterface::class)); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** @var LockableEntity $lockable */ |
135
|
1 |
|
$lockable = $this->getLockableEntity($entity); |
136
|
|
|
|
137
|
1 |
|
return array_reduce( |
138
|
1 |
|
$this->getEntityVersionLocksByLockableEntity($lockable, $userToExclude), |
139
|
|
|
function ($return, EntityVersionLock $item) { |
140
|
|
|
$return[] = $item->getOwner(); |
141
|
|
|
|
142
|
|
|
return $return; |
143
|
|
|
}, |
144
|
|
|
[] |
145
|
|
|
); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @param LockableEntity $entity |
150
|
|
|
*/ |
151
|
2 |
|
protected function removeExpiredLocks(LockableEntity $entity) |
152
|
|
|
{ |
153
|
2 |
|
$locks = $this->objectManager->getRepository(EntityVersionLock::class)->getExpiredLocks($entity, $this->threshold); |
|
|
|
|
154
|
2 |
|
foreach ($locks as $lock) { |
155
|
2 |
|
$this->objectManager->remove($lock); |
156
|
|
|
} |
157
|
2 |
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* When editing an entity, check if there is a lock for this entity. |
161
|
|
|
* |
162
|
|
|
* @param LockableEntity $entity |
163
|
|
|
* @param User $userToExclude |
|
|
|
|
164
|
|
|
* |
165
|
|
|
* @return EntityVersionLock[] |
|
|
|
|
166
|
|
|
*/ |
167
|
3 |
|
protected function getEntityVersionLocksByLockableEntity(LockableEntity $entity, /*\Kunstmaan\AdminBundle\Entity\UserInterface*/ $userToExclude = null) |
168
|
|
|
{ |
169
|
|
|
// NEXT_MAJOR: remove type check and enable parameter typehint |
170
|
3 |
View Code Duplication |
if (!$userToExclude instanceof User && !$userToExclude instanceof UserInterface) { |
|
|
|
|
171
|
|
|
throw new \InvalidArgumentException(sprintf('The "$userToExclude" argument must be of type "%s" or implement the "%s" interface', User::class, UserInterface::class)); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** @var EntityVersionLockRepository $objectRepository */ |
175
|
3 |
|
$objectRepository = $this->objectManager->getRepository(EntityVersionLock::class); |
176
|
|
|
|
177
|
3 |
|
return $objectRepository->getLocksForLockableEntity($entity, $this->threshold, $userToExclude); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Get or create a LockableEntity for an entity with LockableEntityInterface |
182
|
|
|
* |
183
|
|
|
* @param LockableEntityInterface $entity |
184
|
|
|
* |
185
|
|
|
* @return LockableEntity |
186
|
|
|
*/ |
187
|
6 |
|
protected function getLockableEntity(LockableEntityInterface $entity, $create = true) |
188
|
|
|
{ |
189
|
|
|
/** @var LockableEntity $lockable */ |
190
|
6 |
|
$lockable = $this->objectManager->getRepository(LockableEntity::class)->getOrCreate($entity->getId(), \get_class($entity)); |
|
|
|
|
191
|
|
|
|
192
|
6 |
|
if ($create === true && $lockable->getId() === null) { |
193
|
|
|
$this->objectManager->persist($lockable); |
194
|
|
|
$this->objectManager->flush(); |
195
|
|
|
} |
196
|
|
|
|
197
|
6 |
|
return $lockable; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @param ObjectManager $objectManager |
202
|
|
|
*/ |
203
|
6 |
|
public function setObjectManager($objectManager) |
204
|
|
|
{ |
205
|
6 |
|
$this->objectManager = $objectManager; |
206
|
6 |
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @param int $threshold |
210
|
|
|
*/ |
211
|
6 |
|
public function setThreshold($threshold) |
212
|
|
|
{ |
213
|
6 |
|
$this->threshold = $threshold; |
214
|
6 |
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param bool lockEnabled |
218
|
|
|
*/ |
219
|
6 |
|
public function setLockEnabled($lockEnabled) |
220
|
|
|
{ |
221
|
6 |
|
$this->lockEnabled = $lockEnabled; |
222
|
6 |
|
} |
223
|
|
|
} |
224
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.