1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Polder Knowledge / entityservice (https://polderknowledge.com) |
4
|
|
|
* |
5
|
|
|
* @link https://github.com/polderknowledge/entityservice for the canonical source repository |
6
|
|
|
* @copyright Copyright (c) 2016 Polder Knowledge (https://polderknowledge.com) |
7
|
|
|
* @license https://github.com/polderknowledge/entityservice/blob/master/LICENSE.md MIT |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace PolderKnowledge\EntityService; |
11
|
|
|
|
12
|
|
|
use Doctrine\Common\Collections\Collection; |
13
|
|
|
use Doctrine\Common\Collections\Criteria; |
14
|
|
|
use PolderKnowledge\EntityService\Event\EntityEvent; |
15
|
|
|
use PolderKnowledge\EntityService\Exception\RuntimeException; |
16
|
|
|
use PolderKnowledge\EntityService\Exception\ServiceException; |
17
|
|
|
use PolderKnowledge\EntityService\Feature\TransactionAwareInterface; |
18
|
|
|
use PolderKnowledge\EntityService\Repository\EntityRepositoryInterface; |
19
|
|
|
use PolderKnowledge\EntityService\Repository\Feature\DeletableInterface; |
20
|
|
|
use PolderKnowledge\EntityService\Repository\Feature\FlushableInterface; |
21
|
|
|
use PolderKnowledge\EntityService\Repository\Feature\ReadableInterface; |
22
|
|
|
use PolderKnowledge\EntityService\Repository\Feature\WritableInterface; |
23
|
|
|
use Traversable; |
24
|
|
|
use Zend\EventManager\AbstractListenerAggregate; |
25
|
|
|
use Zend\EventManager\EventManager; |
26
|
|
|
use Zend\EventManager\EventManagerInterface; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Base class for application specific EntityServices. This is a fully event driven class. |
30
|
|
|
* Each method trigger a method for extendability. |
31
|
|
|
*/ |
32
|
|
|
abstract class AbstractEntityService extends AbstractListenerAggregate implements |
33
|
|
|
EntityServiceInterface, |
34
|
|
|
TransactionAwareInterface |
35
|
|
|
{ |
36
|
|
|
/** |
37
|
|
|
* The repository that is used for this entity service. |
38
|
|
|
* |
39
|
|
|
* @var EntityRepositoryInterface |
40
|
|
|
*/ |
41
|
|
|
private $repository; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* EventManager handling all events triggered by this service |
45
|
|
|
* |
46
|
|
|
* @var EventManagerInterface |
47
|
|
|
*/ |
48
|
|
|
private $eventManager; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Initialized Event |
52
|
|
|
* |
53
|
|
|
* @var EntityEvent |
54
|
|
|
*/ |
55
|
|
|
private $event; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Initializes a new instance of this class. |
59
|
|
|
* |
60
|
90 |
|
* @param EntityRepositoryInterface $repository The repository that is used to communicate with. |
61
|
|
|
* @param string $entityClassName The FQCN of the entity. |
62
|
90 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
63
|
90 |
|
*/ |
64
|
|
|
public function __construct(EntityRepositoryInterface $repository, $entityClassName) |
65
|
90 |
|
{ |
66
|
90 |
|
$this->repository = $repository; |
67
|
|
|
$this->listeners = []; |
68
|
|
|
|
69
|
|
|
$this->getEvent()->setEntityClassName($entityClassName); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
75 |
|
* Gets the repository that is used by the service. |
74
|
|
|
* |
75
|
75 |
|
* @return EntityRepositoryInterface |
76
|
|
|
*/ |
77
|
|
|
protected function getRepository() |
78
|
|
|
{ |
79
|
|
|
return $this->repository; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
90 |
|
* Get the pre initialized event object |
84
|
|
|
* |
85
|
90 |
|
* @return EntityEvent |
86
|
90 |
|
*/ |
87
|
|
|
protected function getEvent() |
88
|
90 |
|
{ |
89
|
90 |
|
if (null === $this->event) { |
90
|
|
|
$this->event = $event = new EntityEvent; |
91
|
90 |
|
|
92
|
|
|
$event->setTarget($this); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
return $this->event; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Will create an EventManager when no EventManager was provided. |
100
|
45 |
|
* The returned EventManager is used to handle events triggered by this service instance. |
101
|
|
|
* |
102
|
45 |
|
* @return EventManagerInterface |
103
|
36 |
|
*/ |
104
|
36 |
|
public function getEventManager() |
105
|
|
|
{ |
106
|
45 |
|
if (null === $this->eventManager) { |
107
|
|
|
$this->setEventManager(new EventManager); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $this->eventManager; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Set the EventManager used by this service instance to handle its events. |
115
|
|
|
* It will take care of disabling the old EventManager and will subscribe the internal |
116
|
45 |
|
* listeners to the new EventManager |
117
|
|
|
* |
118
|
45 |
|
* @param EventManagerInterface $eventManager |
119
|
3 |
|
*/ |
120
|
|
|
public function setEventManager(EventManagerInterface $eventManager) |
121
|
|
|
{ |
122
|
45 |
|
if ($this->eventManager === $eventManager || $eventManager === null) { |
123
|
3 |
|
return; |
124
|
3 |
|
} |
125
|
|
|
|
126
|
45 |
|
if ($this->eventManager !== null) { |
127
|
45 |
|
$this->detach($this->eventManager); |
128
|
45 |
|
} |
129
|
45 |
|
|
130
|
45 |
|
$this->eventManager = $eventManager; |
131
|
45 |
|
$this->eventManager->addIdentifiers([ |
132
|
45 |
|
'EntityService', |
133
|
|
|
'PolderKnowledge\EntityService\Service\EntityService', |
134
|
45 |
|
$this->getEntityServiceName(), |
135
|
45 |
|
trim($this->getEntityServiceName(), '\\'), |
136
|
|
|
]); |
137
|
|
|
|
138
|
|
|
$this->attach($this->eventManager); |
139
|
|
|
} |
140
|
45 |
|
|
141
|
|
|
/** |
142
|
|
|
* {@inheritDoc} |
143
|
27 |
|
*/ |
144
|
|
|
public function attach(EventManagerInterface $events, $priority = 1) |
145
|
27 |
|
{ |
146
|
27 |
View Code Duplication |
$callback = function (EntityEvent $event) { |
|
|
|
|
147
|
27 |
|
$repository = $event->getTarget()->getRepository(); |
148
|
27 |
|
|
149
|
45 |
|
$event->setResult(call_user_func_array( |
150
|
|
|
[$repository, $event->getName()], |
151
|
45 |
|
$event->getParams() |
152
|
45 |
|
)); |
153
|
45 |
|
}; |
154
|
45 |
|
|
155
|
45 |
|
$this->listeners[] = $events->attach('countBy', $callback, 0); |
156
|
45 |
|
$this->listeners[] = $events->attach('delete', $callback, 0); |
157
|
45 |
|
$this->listeners[] = $events->attach('deleteBy', $callback, 0); |
158
|
45 |
|
$this->listeners[] = $events->attach('find', $callback, 0); |
159
|
45 |
|
$this->listeners[] = $events->attach('findAll', $callback, 0); |
160
|
|
|
$this->listeners[] = $events->attach('findBy', $callback, 0); |
161
|
45 |
|
$this->listeners[] = $events->attach('findOneBy', $callback, 0); |
162
|
27 |
|
$this->listeners[] = $events->attach( |
163
|
45 |
|
'persist', |
164
|
45 |
View Code Duplication |
function (EntityEvent $event) { |
|
|
|
|
165
|
|
|
$repository = $event->getTarget()->getRepository(); |
166
|
|
|
|
167
|
|
|
call_user_func_array([$repository, 'persist'], $event->getParams()); |
168
|
|
|
|
169
|
|
|
if ($repository instanceof FlushableInterface) { |
170
|
|
|
$repository->flush(); |
171
|
81 |
|
} |
172
|
|
|
}, |
173
|
81 |
|
0 |
174
|
|
|
); |
175
|
|
|
$this->listeners[] = $events->attach( |
176
|
|
|
'multiPersist', |
177
|
|
|
function (EntityEvent $event) { |
178
|
|
|
$repository = $event->getTarget()->getRepository(); |
179
|
|
|
|
180
|
|
|
$entities = current($event->getParams()); |
181
|
|
|
|
182
|
6 |
|
foreach ($entities as $entity) { |
183
|
|
|
call_user_func_array([$repository, 'persist'], [$entity]); |
184
|
6 |
|
} |
185
|
3 |
|
|
186
|
|
|
if ($repository instanceof FlushableInterface) { |
187
|
|
|
$repository->flush(); |
188
|
3 |
|
} |
189
|
3 |
|
}, |
190
|
3 |
|
0 |
191
|
|
|
); |
192
|
|
|
|
193
|
|
|
$this->listeners[] = $events->attach('*', function (EntityEvent $event) { |
194
|
|
|
$event->disableStoppingOfPropagation(); |
195
|
|
|
}, -1); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
6 |
|
* Returns the FQCN of the entity handled by this service. |
200
|
|
|
* |
201
|
6 |
|
* @return string |
202
|
3 |
|
*/ |
203
|
|
|
protected function getEntityServiceName() |
204
|
|
|
{ |
205
|
3 |
|
return $this->getEvent()->getEntityClassName(); |
206
|
3 |
|
} |
207
|
3 |
|
|
208
|
|
|
/** |
209
|
|
|
* Deletes the given object from the repository |
210
|
|
|
* |
211
|
|
|
* @param object $entity The entity to delete. |
212
|
|
|
* @return mixed |
213
|
|
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
214
|
|
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
215
|
|
|
*/ |
216
|
6 |
|
public function delete($entity) |
217
|
|
|
{ |
218
|
6 |
|
if (!$this->isRepositoryDeletable()) { |
219
|
3 |
|
throw $this->createNotDeletableException(); |
220
|
|
|
} |
221
|
|
|
|
222
|
3 |
|
return $this->trigger(__FUNCTION__, [ |
223
|
3 |
|
'entity' => $entity, |
224
|
3 |
|
]); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* Deletes all objects matching the criteria from the repository |
229
|
|
|
* |
230
|
|
|
* @param array|Criteria $criteria The criteria values to match on. |
231
|
|
|
* @return mixed |
232
|
|
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
233
|
9 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
234
|
|
|
*/ |
235
|
9 |
|
public function deleteBy($criteria) |
236
|
3 |
|
{ |
237
|
|
|
if (!$this->isRepositoryDeletable()) { |
238
|
|
|
throw $this->createNotDeletableException(); |
239
|
6 |
|
} |
240
|
6 |
|
|
241
|
6 |
|
return $this->trigger(__FUNCTION__, [ |
242
|
|
|
'criteria' => $criteria, |
243
|
|
|
]); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Count the objects matching the criteria respecting the order, limit and offset. |
248
|
|
|
* |
249
|
6 |
|
* @param array|Criteria $criteria The criteria values to match on. |
250
|
|
|
* @return int |
251
|
6 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
252
|
3 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
253
|
|
|
*/ |
254
|
|
|
public function countBy($criteria) |
255
|
3 |
|
{ |
256
|
|
|
if (!$this->isRepositoryReadable()) { |
257
|
|
|
throw $this->createNotReadableException(); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
return $this->trigger(__FUNCTION__, [ |
261
|
|
|
'criteria' => $criteria, |
262
|
|
|
]); |
263
|
|
|
} |
264
|
6 |
|
|
265
|
|
|
/** |
266
|
6 |
|
* Find one object in the repository matching the $id |
267
|
3 |
|
* |
268
|
|
|
* @param mixed $id The id of the entity. |
269
|
|
|
* @return object|null |
270
|
3 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
271
|
3 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
272
|
3 |
|
*/ |
273
|
|
|
public function find($id) |
274
|
|
|
{ |
275
|
|
|
if (!$this->isRepositoryReadable()) { |
276
|
|
|
throw $this->createNotReadableException(); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
return $this->trigger(__FUNCTION__, [ |
280
|
|
|
'id' => $id, |
281
|
6 |
|
]); |
282
|
|
|
} |
283
|
6 |
|
|
284
|
3 |
|
/** |
285
|
|
|
* Finds all entities in the repository. |
286
|
|
|
* |
287
|
3 |
|
* @return array Returns the entities that exist. |
288
|
3 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
289
|
3 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
290
|
|
|
*/ |
291
|
|
|
public function findAll() |
292
|
|
|
{ |
293
|
|
|
if (!$this->isRepositoryReadable()) { |
294
|
|
|
throw $this->createNotReadableException(); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
return $this->trigger(__FUNCTION__, []); |
298
|
|
|
} |
299
|
6 |
|
|
300
|
|
|
/** |
301
|
6 |
|
* Find one or more objects in the repository matching the criteria respecting the order, limit and offset |
302
|
3 |
|
* |
303
|
|
|
* @param array|Criteria $criteria The array with criteria to search on. |
304
|
|
|
* @return array |
305
|
3 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
306
|
3 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
307
|
3 |
|
*/ |
308
|
|
|
public function findBy($criteria) |
309
|
|
|
{ |
310
|
|
|
if (!$this->isRepositoryReadable()) { |
311
|
|
|
throw $this->createNotReadableException(); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
return $this->trigger(__FUNCTION__, [ |
315
|
|
|
'criteria' => $criteria, |
316
|
|
|
]); |
317
|
6 |
|
} |
318
|
|
|
|
319
|
6 |
|
/** |
320
|
3 |
|
* Find one object in the repository matching the criteria |
321
|
|
|
* |
322
|
|
|
* @param array|Criteria $criteria The criteria values to match on. |
323
|
3 |
|
* @return object|null |
324
|
3 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
325
|
3 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
326
|
|
|
*/ |
327
|
|
|
public function findOneBy($criteria) |
328
|
|
|
{ |
329
|
|
|
if (!$this->isRepositoryReadable()) { |
330
|
|
|
throw $this->createNotReadableException(); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
return $this->trigger(__FUNCTION__, [ |
334
|
|
|
'criteria' => $criteria, |
335
|
30 |
|
]); |
336
|
|
|
} |
337
|
30 |
|
|
338
|
30 |
|
/** |
339
|
30 |
|
* Persist the given entity |
340
|
|
|
* |
341
|
30 |
|
* @param object $entity |
342
|
|
|
* @return mixed |
343
|
30 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
344
|
3 |
|
* @throws RuntimeException |
345
|
|
|
*/ |
346
|
|
|
public function persist($entity) |
347
|
27 |
|
{ |
348
|
|
|
if (!$this->isRepositoryWritable()) { |
349
|
|
|
throw $this->createNotWritableException(); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
return $this->trigger(__FUNCTION__, [ |
353
|
|
|
'entity' => $entity, |
354
|
|
|
]); |
355
|
6 |
|
} |
356
|
|
|
|
357
|
6 |
|
/** |
358
|
3 |
|
* Persist the given object and flushes it to the storage device. |
359
|
3 |
|
* |
360
|
3 |
|
* @param array|Collection|Traversable $entities The entities to persist. |
361
|
3 |
|
* @return mixed |
362
|
|
|
* @throws RuntimeException |
363
|
|
|
*/ |
364
|
3 |
|
public function multiPersist($entities) |
365
|
3 |
|
{ |
366
|
|
|
if (!$this->isRepositoryWritable()) { |
367
|
|
|
throw $this->createNotWritableException(); |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
return $this->trigger(__FUNCTION__, [ |
371
|
|
|
'entities' => $entities, |
372
|
6 |
|
]); |
373
|
|
|
} |
374
|
6 |
|
|
375
|
3 |
|
/** |
376
|
3 |
|
* will prepare the event object and trigger the event using the internal EventManager |
377
|
3 |
|
* |
378
|
3 |
|
* @param string $name |
379
|
|
|
* @param array $params |
380
|
|
|
* @return mixed |
381
|
3 |
|
* @throws \Zend\EventManager\Exception\InvalidArgumentException |
382
|
3 |
|
* @throws \PolderKnowledge\EntityService\Exception\RuntimeException |
383
|
|
|
*/ |
384
|
|
|
protected function trigger($name, array $params) |
385
|
|
|
{ |
386
|
|
|
$event = clone $this->getEvent(); |
387
|
|
|
$event->setName($name); |
388
|
|
|
$event->setParams($params); |
389
|
6 |
|
|
390
|
|
|
$responseCollection = $this->getEventManager()->triggerEvent($event); |
391
|
6 |
|
|
392
|
3 |
|
if ($responseCollection->stopped() && $event->isError()) { |
393
|
3 |
|
throw new RuntimeException($event->getError(), $event->getErrorNr()); |
394
|
3 |
|
} |
395
|
3 |
|
|
396
|
|
|
return $event->getResult(); |
397
|
|
|
} |
398
|
3 |
|
|
399
|
3 |
|
/** |
400
|
|
|
* Starts a new transaction. |
401
|
|
|
* |
402
|
|
|
* @throws ServiceException |
403
|
|
|
*/ |
404
|
18 |
|
public function beginTransaction() |
405
|
|
|
{ |
406
|
18 |
|
if ($this->isTransactionEnabled() === false) { |
407
|
|
|
throw new ServiceException(sprintf( |
408
|
|
|
'The repository for %s doesn\'t support Transactions', |
409
|
|
|
$this->getEntityServiceName() |
410
|
|
|
)); |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
$this->getRepository()->beginTransaction(); |
414
|
12 |
|
} |
415
|
|
|
|
416
|
12 |
|
/** |
417
|
|
|
* Commits a started transaction. |
418
|
|
|
* |
419
|
|
|
* @throws ServiceException |
420
|
|
|
*/ |
421
|
|
|
public function commitTransaction() |
422
|
|
|
{ |
423
|
|
|
if ($this->isTransactionEnabled() === false) { |
424
|
33 |
|
throw new ServiceException(sprintf( |
425
|
|
|
'The repository for %s doesn\'t support Transactions', |
426
|
33 |
|
$this->getEntityServiceName() |
427
|
|
|
)); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
$this->getRepository()->commitTransaction(); |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
/** |
434
|
12 |
|
* Rolls back a started transaction. |
435
|
|
|
* |
436
|
12 |
|
* @throws ServiceException |
437
|
|
|
*/ |
438
|
|
|
public function rollbackTransaction() |
439
|
|
|
{ |
440
|
|
|
if ($this->isTransactionEnabled() === false) { |
441
|
|
|
throw new ServiceException(sprintf( |
442
|
|
|
'The repository for %s doesn\'t support Transactions', |
443
|
|
|
$this->getEntityServiceName() |
444
|
6 |
|
)); |
445
|
|
|
} |
446
|
6 |
|
|
447
|
6 |
|
$this->getRepository()->rollbackTransaction(); |
448
|
6 |
|
} |
449
|
6 |
|
|
450
|
|
|
/** |
451
|
|
|
* Returns true when possible to start an transaction |
452
|
|
|
*/ |
453
|
|
|
public function isTransactionEnabled() |
454
|
|
|
{ |
455
|
|
|
return $this->getRepository() instanceof TransactionAwareInterface; |
456
|
|
|
} |
457
|
15 |
|
|
458
|
|
|
/** |
459
|
15 |
|
* Returns true when the repository for $entityName is writable |
460
|
15 |
|
* |
461
|
15 |
|
* @return bool |
462
|
15 |
|
*/ |
463
|
|
|
protected function isRepositoryWritable() |
464
|
|
|
{ |
465
|
|
|
return $this->getRepository() instanceof WritableInterface; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Returns true when the repository for $entityName is readable |
470
|
6 |
|
* |
471
|
|
|
* @return bool |
472
|
6 |
|
*/ |
473
|
6 |
|
protected function isRepositoryReadable() |
474
|
6 |
|
{ |
475
|
6 |
|
return $this->getRepository() instanceof ReadableInterface; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Returns true when the repository for $entityName has delete behavior |
480
|
|
|
* |
481
|
|
|
* @return bool |
482
|
|
|
*/ |
483
|
|
|
protected function isRepositoryDeletable() |
484
|
|
|
{ |
485
|
|
|
return $this->getRepository() instanceof DeletableInterface; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Throws an exception for cases where it's not possible to delete from the repository. |
490
|
|
|
* |
491
|
|
|
* @throws RuntimeException |
492
|
|
|
*/ |
493
|
|
|
private function createNotDeletableException() |
494
|
|
|
{ |
495
|
|
|
throw new RuntimeException(sprintf( |
496
|
|
|
'The entities of type "%s" cannot be deleted from its repository.', |
497
|
|
|
$this->getEntityServiceName() |
498
|
|
|
)); |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
/** |
502
|
|
|
* Throws an exception for cases where it's not possible to read from the repository. |
503
|
|
|
* |
504
|
|
|
* @throws RuntimeException |
505
|
|
|
*/ |
506
|
|
|
private function createNotReadableException() |
507
|
|
|
{ |
508
|
|
|
throw new RuntimeException(sprintf( |
509
|
|
|
'It is not possible to read entities of type "%s" from its repository.', |
510
|
|
|
$this->getEntityServiceName() |
511
|
|
|
)); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* Throws an exception for cases where it's not possible to write to the repository. |
516
|
|
|
* |
517
|
|
|
* @throws RuntimeException |
518
|
|
|
*/ |
519
|
|
|
private function createNotWritableException() |
520
|
|
|
{ |
521
|
|
|
throw new RuntimeException(sprintf( |
522
|
|
|
'The entities of type "%s" cannot be written to its repository.', |
523
|
|
|
$this->getEntityServiceName() |
524
|
|
|
)); |
525
|
|
|
} |
526
|
|
|
} |
527
|
|
|
|
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.