|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace LAG\AdminBundle\Admin; |
|
4
|
|
|
|
|
5
|
|
|
use Doctrine\Common\Collections\Collection; |
|
6
|
|
|
use LAG\AdminBundle\Action\ActionInterface; |
|
7
|
|
|
use LAG\AdminBundle\Admin\Behaviors\AdminTrait; |
|
8
|
|
|
use LAG\AdminBundle\Admin\Configuration\AdminConfiguration; |
|
9
|
|
|
use Doctrine\Common\Collections\ArrayCollection; |
|
10
|
|
|
use Exception; |
|
11
|
|
|
use LAG\AdminBundle\Admin\Request\LoadParameterExtractor; |
|
12
|
|
|
use LAG\AdminBundle\Admin\Request\RequestHandler; |
|
13
|
|
|
use LAG\AdminBundle\DataProvider\DataProviderInterface; |
|
14
|
|
|
use LAG\AdminBundle\DataProvider\Loader\EntityLoaderInterface; |
|
15
|
|
|
use LAG\AdminBundle\LAGAdminBundle; |
|
16
|
|
|
use LAG\AdminBundle\Message\MessageHandlerInterface; |
|
17
|
|
|
use LAG\AdminBundle\View\Factory\ViewFactory; |
|
18
|
|
|
use LAG\AdminBundle\View\ViewInterface; |
|
19
|
|
|
use LogicException; |
|
20
|
|
|
use Pagerfanta\Pagerfanta; |
|
21
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
22
|
|
|
use Symfony\Component\Form\Test\FormInterface; |
|
23
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
24
|
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
|
25
|
|
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
|
26
|
|
|
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; |
|
27
|
|
|
use Symfony\Component\Security\Core\Exception\AccessDeniedException; |
|
28
|
|
|
use Symfony\Component\Security\Core\User\UserInterface; |
|
29
|
|
|
|
|
30
|
|
|
class Admin implements AdminInterface |
|
31
|
|
|
{ |
|
32
|
|
|
use AdminTrait; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Entities collection. |
|
36
|
|
|
* |
|
37
|
|
|
* @var ArrayCollection |
|
38
|
|
|
*/ |
|
39
|
|
|
protected $entities; |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* @var MessageHandlerInterface |
|
43
|
|
|
*/ |
|
44
|
|
|
protected $messageHandler; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @var DataProviderInterface |
|
48
|
|
|
*/ |
|
49
|
|
|
protected $dataProvider; |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Admin configuration object |
|
53
|
|
|
* |
|
54
|
|
|
* @var AdminConfiguration |
|
55
|
|
|
*/ |
|
56
|
|
|
protected $configuration; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* Admin configured actions |
|
60
|
|
|
* |
|
61
|
|
|
* @var ActionInterface[] |
|
62
|
|
|
*/ |
|
63
|
|
|
protected $actions = []; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Admin current action. It will be set after calling the handleRequest() |
|
67
|
|
|
* |
|
68
|
|
|
* @var ActionInterface |
|
69
|
|
|
*/ |
|
70
|
|
|
protected $currentAction; |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* Admin name |
|
74
|
|
|
* |
|
75
|
|
|
* @var string |
|
76
|
|
|
*/ |
|
77
|
|
|
protected $name; |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* @var EventDispatcherInterface |
|
81
|
|
|
*/ |
|
82
|
|
|
protected $eventDispatcher; |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* @var AuthorizationCheckerInterface |
|
86
|
|
|
*/ |
|
87
|
|
|
protected $authorizationChecker; |
|
88
|
|
|
|
|
89
|
|
|
/** |
|
90
|
|
|
* @var TokenStorageInterface |
|
91
|
|
|
*/ |
|
92
|
|
|
protected $tokenStorage; |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* @var FormInterface|null |
|
96
|
|
|
*/ |
|
97
|
|
|
protected $filterForm; |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* @var EntityLoaderInterface |
|
101
|
|
|
*/ |
|
102
|
|
|
protected $entityLoader; |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* @var ViewFactory |
|
106
|
|
|
*/ |
|
107
|
|
|
private $viewFactory; |
|
108
|
|
|
|
|
109
|
|
|
/** |
|
110
|
|
|
* @var ViewInterface |
|
111
|
|
|
*/ |
|
112
|
|
|
private $view; |
|
113
|
|
|
/** |
|
114
|
|
|
* @var RequestHandler |
|
115
|
|
|
*/ |
|
116
|
|
|
private $requestHandler; |
|
117
|
|
|
|
|
118
|
|
|
/** |
|
119
|
|
|
* Admin constructor. |
|
120
|
|
|
* |
|
121
|
|
|
* @param string $name |
|
122
|
|
|
* @param EntityLoaderInterface $entityLoader |
|
123
|
|
|
* @param AdminConfiguration $configuration |
|
124
|
|
|
* @param MessageHandlerInterface $messageHandler |
|
125
|
|
|
* @param EventDispatcherInterface $eventDispatcher |
|
126
|
|
|
* @param AuthorizationCheckerInterface $authorizationChecker |
|
127
|
|
|
* @param TokenStorageInterface $tokenStorage |
|
128
|
|
|
* @param RequestHandler $requestHandler |
|
129
|
|
|
* @param ViewFactory $viewFactory |
|
130
|
|
|
* @param array $actions |
|
131
|
|
|
*/ |
|
132
|
|
View Code Duplication |
public function __construct( |
|
|
|
|
|
|
133
|
|
|
$name, |
|
134
|
|
|
EntityLoaderInterface $entityLoader, |
|
135
|
|
|
AdminConfiguration $configuration, |
|
136
|
|
|
MessageHandlerInterface $messageHandler, |
|
137
|
|
|
EventDispatcherInterface $eventDispatcher, |
|
138
|
|
|
AuthorizationCheckerInterface $authorizationChecker, |
|
139
|
|
|
TokenStorageInterface $tokenStorage, |
|
140
|
|
|
RequestHandler $requestHandler, |
|
141
|
|
|
ViewFactory $viewFactory, |
|
142
|
|
|
$actions = [] |
|
143
|
|
|
) { |
|
144
|
|
|
$this->name = $name; |
|
145
|
|
|
$this->entities = new ArrayCollection(); |
|
146
|
|
|
$this->configuration = $configuration; |
|
147
|
|
|
$this->messageHandler = $messageHandler; |
|
148
|
|
|
$this->eventDispatcher = $eventDispatcher; |
|
149
|
|
|
$this->authorizationChecker = $authorizationChecker; |
|
150
|
|
|
$this->tokenStorage = $tokenStorage; |
|
151
|
|
|
$this->actions = $actions; |
|
152
|
|
|
$this->entityLoader = $entityLoader; |
|
153
|
|
|
$this->dataProvider = $entityLoader->getDataProvider(); |
|
154
|
|
|
$this->viewFactory = $viewFactory; |
|
155
|
|
|
$this->requestHandler = $requestHandler; |
|
156
|
|
|
} |
|
157
|
|
|
|
|
158
|
|
|
/** |
|
159
|
|
|
* Load entities and set current action according to request and the optional filters. |
|
160
|
|
|
* |
|
161
|
|
|
* @param Request $request |
|
162
|
|
|
* @param array $filters |
|
163
|
|
|
*/ |
|
164
|
|
|
public function handleRequest(Request $request, array $filters = []) |
|
165
|
|
|
{ |
|
166
|
|
|
if (!$this->requestHandler->supports($request)) { |
|
167
|
|
|
throw new BadRequestHttpException( |
|
168
|
|
|
'The given request can be processed. The route parameters "_admin" and "_action" probably missing' |
|
169
|
|
|
); |
|
170
|
|
|
} |
|
171
|
|
|
$actionName = $request->get('_route_params')[LAGAdminBundle::REQUEST_PARAMETER_ACTION]; |
|
172
|
|
|
|
|
173
|
|
|
$this->view = $this |
|
174
|
|
|
->viewFactory |
|
175
|
|
|
->create($actionName, $this->name, $this->configuration, $this->configuration->getParameter('actions')[$actionName]) |
|
176
|
|
|
; |
|
177
|
|
|
|
|
178
|
|
|
// set current action |
|
|
|
|
|
|
179
|
|
|
// $this->currentAction = $this->getAction( |
|
180
|
|
|
// $request->get('_route_params')[LAGAdminBundle::REQUEST_PARAMETER_ACTION] |
|
181
|
|
|
// ); |
|
182
|
|
|
|
|
183
|
|
|
// check if user is logged have required permissions to get current action |
|
184
|
|
|
$this->checkPermissions(); |
|
185
|
|
|
|
|
186
|
|
|
// get the current action configuration bag |
|
187
|
|
|
$actionConfiguration = $this |
|
188
|
|
|
->view |
|
189
|
|
|
->getConfiguration() |
|
190
|
|
|
; |
|
191
|
|
|
|
|
192
|
|
|
// if no loading is required, no more thing to do. Some actions do not require to load entities from |
|
193
|
|
|
// the DataProvider |
|
194
|
|
|
if (Admin::LOAD_STRATEGY_NONE === $actionConfiguration->getParameter('load_strategy')) { |
|
195
|
|
|
return; |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
// retrieve the criteria to find one or more entities (from the request for sorting, pagination... and from |
|
199
|
|
|
// the filter form |
|
200
|
|
|
$loader = new LoadParameterExtractor($actionConfiguration, $filters); |
|
201
|
|
|
$loader->load($request); |
|
202
|
|
|
|
|
203
|
|
|
// load entities according to action and request |
|
204
|
|
|
$this |
|
205
|
|
|
->entityLoader |
|
206
|
|
|
->configure($actionConfiguration) |
|
207
|
|
|
; |
|
208
|
|
|
|
|
209
|
|
|
$this |
|
210
|
|
|
->load( |
|
211
|
|
|
$loader->getCriteria(), |
|
212
|
|
|
$loader->getOrder(), |
|
213
|
|
|
$loader->getMaxPerPage(), |
|
214
|
|
|
$loader->getPage() |
|
215
|
|
|
) |
|
216
|
|
|
; |
|
217
|
|
|
$this->view->setEntities($this->entities); |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
/** |
|
221
|
|
|
* Check if user is allowed to be here. |
|
222
|
|
|
* |
|
223
|
|
|
* @throws LogicException|AccessDeniedException |
|
224
|
|
|
*/ |
|
225
|
|
|
public function checkPermissions() |
|
226
|
|
|
{ |
|
227
|
|
|
$user = $this |
|
228
|
|
|
->tokenStorage |
|
229
|
|
|
->getToken() |
|
230
|
|
|
->getUser() |
|
231
|
|
|
; |
|
232
|
|
|
|
|
233
|
|
|
// must be authenticated to access to an admin |
|
234
|
|
|
if (!($user instanceof UserInterface)) { |
|
235
|
|
|
throw new AccessDeniedException(); |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
|
|
// a view must have been defined |
|
239
|
|
|
if (null === $this->view) { |
|
240
|
|
|
throw new LogicException( |
|
241
|
|
|
'A view must be defined before checking the permissions. Maybe you forget to call handleRequest()' |
|
242
|
|
|
); |
|
243
|
|
|
} |
|
244
|
|
|
// check if the current User is granted in Symfony security configuration |
|
245
|
|
|
if (!$this->authorizationChecker->isGranted($user->getRoles(), $user)) { |
|
246
|
|
|
throw new AccessDeniedException(); |
|
247
|
|
|
} |
|
248
|
|
|
$permissions = $this |
|
249
|
|
|
->view |
|
250
|
|
|
->getConfiguration() |
|
251
|
|
|
->getParameter('permissions') |
|
252
|
|
|
; |
|
253
|
|
|
|
|
254
|
|
|
// check if the User is granted according to Admin configuration |
|
255
|
|
|
if (!$this->authorizationChecker->isGranted($permissions, $user)) { |
|
256
|
|
|
throw new AccessDeniedException(); |
|
257
|
|
|
} |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
/** |
|
261
|
|
|
* Create and return a new entity. |
|
262
|
|
|
* |
|
263
|
|
|
* @return object |
|
264
|
|
|
*/ |
|
265
|
|
|
public function create() |
|
266
|
|
|
{ |
|
267
|
|
|
// create an entity from the data provider |
|
268
|
|
|
$entity = $this |
|
269
|
|
|
->dataProvider |
|
270
|
|
|
->create(); |
|
271
|
|
|
|
|
272
|
|
|
// add it to the collection |
|
273
|
|
|
$this |
|
274
|
|
|
->entities |
|
275
|
|
|
->add($entity); |
|
276
|
|
|
|
|
277
|
|
|
return $entity; |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
|
/** |
|
281
|
|
|
* Save entity via admin manager. |
|
282
|
|
|
*/ |
|
283
|
|
|
public function save() |
|
284
|
|
|
{ |
|
285
|
|
|
foreach ($this->entities as $entity) { |
|
286
|
|
|
$this |
|
287
|
|
|
->dataProvider |
|
288
|
|
|
->save($entity) |
|
289
|
|
|
; |
|
290
|
|
|
} |
|
291
|
|
|
// inform the user that the entity is saved |
|
292
|
|
|
$this |
|
293
|
|
|
->messageHandler |
|
294
|
|
|
->handleSuccess($this->generateMessageTranslationKey('saved')) |
|
295
|
|
|
; |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
/** |
|
299
|
|
|
* Remove an entity with data provider. |
|
300
|
|
|
*/ |
|
301
|
|
|
public function remove() |
|
302
|
|
|
{ |
|
303
|
|
|
foreach ($this->entities as $entity) { |
|
304
|
|
|
$this |
|
305
|
|
|
->dataProvider |
|
306
|
|
|
->remove($entity); |
|
307
|
|
|
} |
|
308
|
|
|
// inform the user that the entity is removed |
|
309
|
|
|
$this |
|
310
|
|
|
->messageHandler |
|
311
|
|
|
->handleSuccess($this->generateMessageTranslationKey('deleted')) |
|
312
|
|
|
; |
|
313
|
|
|
} |
|
314
|
|
|
|
|
315
|
|
|
/** |
|
316
|
|
|
* Return the number of entities managed by the Admin. |
|
317
|
|
|
* |
|
318
|
|
|
* @return int |
|
319
|
|
|
*/ |
|
320
|
|
|
public function count() |
|
321
|
|
|
{ |
|
322
|
|
|
$count = $this |
|
323
|
|
|
->dataProvider |
|
324
|
|
|
->count() |
|
325
|
|
|
; |
|
326
|
|
|
|
|
327
|
|
|
if (!is_integer($count)) { |
|
328
|
|
|
throw new LogicException( |
|
329
|
|
|
'The data provider should return an integer for the "count()" method, given : '.gettype($count) |
|
330
|
|
|
); |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
return $count; |
|
334
|
|
|
} |
|
335
|
|
|
|
|
336
|
|
|
/** |
|
337
|
|
|
* Load entities according to the given criteria and the current action configuration. |
|
338
|
|
|
* |
|
339
|
|
|
* @param array $criteria |
|
340
|
|
|
* @param array $orderBy |
|
341
|
|
|
* @param int $limit |
|
342
|
|
|
* @param int $offset |
|
343
|
|
|
*/ |
|
344
|
|
|
public function load(array $criteria, array $orderBy = [], $limit = 25, $offset = 1) |
|
345
|
|
|
{ |
|
346
|
|
|
// retrieve the data using the data provider via the entity loader |
|
347
|
|
|
$entities = $this |
|
348
|
|
|
->entityLoader |
|
349
|
|
|
->load($criteria, $orderBy, $limit, $offset) |
|
350
|
|
|
; |
|
351
|
|
|
|
|
352
|
|
|
// either, we have an instance of Pagerfanta, either we should have an array or a collection |
|
353
|
|
|
if ($entities instanceof Pagerfanta) { |
|
354
|
|
|
// if the entities are inside a pager, we get the result and we set the pager for the view |
|
355
|
|
|
$this->entities = new ArrayCollection($entities->getCurrentPageResults()); |
|
|
|
|
|
|
356
|
|
|
$this->pager = $entities; |
|
357
|
|
|
} else { |
|
358
|
|
|
// the data provider should return an array or a collection of entities. |
|
359
|
|
|
if (!is_array($entities) && !$entities instanceof Collection) { |
|
360
|
|
|
throw new LogicException( |
|
361
|
|
|
'The data provider should return either a collection or an array. Got '.gettype($entities).' instead' |
|
362
|
|
|
); |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
// if an array is provided, transform it to a collection to be more convenient |
|
366
|
|
|
if (is_array($entities)) { |
|
367
|
|
|
$entities = new ArrayCollection($entities); |
|
368
|
|
|
} |
|
369
|
|
|
$this->entities = $entities; |
|
370
|
|
|
} |
|
371
|
|
|
} |
|
372
|
|
|
|
|
373
|
|
|
/** |
|
374
|
|
|
* Return loaded entities |
|
375
|
|
|
* |
|
376
|
|
|
* @return Collection |
|
377
|
|
|
*/ |
|
378
|
|
|
public function getEntities() |
|
379
|
|
|
{ |
|
380
|
|
|
return $this->entities; |
|
381
|
|
|
} |
|
382
|
|
|
|
|
383
|
|
|
/** |
|
384
|
|
|
* Return entity for current admin. If entity does not exist, it throws an exception. |
|
385
|
|
|
* |
|
386
|
|
|
* @return mixed |
|
387
|
|
|
* |
|
388
|
|
|
* @throws Exception |
|
389
|
|
|
*/ |
|
390
|
|
|
public function getUniqueEntity() |
|
391
|
|
|
{ |
|
392
|
|
|
if ($this->entities->count() == 0) { |
|
393
|
|
|
throw new Exception('Entity not found in admin "'.$this->getName().'""'); |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
if ($this->entities->count() > 1) { |
|
397
|
|
|
throw new Exception( |
|
398
|
|
|
'Too much entities found in admin '.$this->getName().' ('.$this->entities->count().' entities found, ' |
|
399
|
|
|
.'expected one). Check the load strategy configuration' |
|
400
|
|
|
); |
|
401
|
|
|
} |
|
402
|
|
|
|
|
403
|
|
|
return $this->entities->first(); |
|
404
|
|
|
} |
|
405
|
|
|
|
|
406
|
|
|
/** |
|
407
|
|
|
* Return admin name |
|
408
|
|
|
* |
|
409
|
|
|
* @return string |
|
410
|
|
|
*/ |
|
411
|
|
|
public function getName() |
|
412
|
|
|
{ |
|
413
|
|
|
return $this->name; |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
/** |
|
417
|
|
|
* Return true if the Action with name $name exists in the Admin. If the method return true, it does not necessarily |
|
418
|
|
|
* means that the action is allowed in the current context. |
|
419
|
|
|
* |
|
420
|
|
|
* @param string $name |
|
421
|
|
|
* |
|
422
|
|
|
* @return boolean |
|
423
|
|
|
*/ |
|
424
|
|
|
public function hasAction($name) |
|
425
|
|
|
{ |
|
426
|
|
|
$actions = $this->configuration->getParameter('actions'); |
|
427
|
|
|
|
|
428
|
|
|
if (!key_exists($name, $actions)) { |
|
429
|
|
|
return false; |
|
430
|
|
|
} |
|
431
|
|
|
|
|
432
|
|
|
return null !== $actions[$name] && false !== $actions[$name]; |
|
433
|
|
|
} |
|
434
|
|
|
|
|
435
|
|
|
public function getView() |
|
436
|
|
|
{ |
|
437
|
|
|
if (!$this->hasView()) { |
|
438
|
|
|
throw new LogicException('The view is not defined. Maybe you forgot to call handleRequest() ?'); |
|
439
|
|
|
} |
|
440
|
|
|
|
|
441
|
|
|
return $this->view; |
|
442
|
|
|
} |
|
443
|
|
|
|
|
444
|
|
|
public function hasView() |
|
445
|
|
|
{ |
|
446
|
|
|
return null !== $this->view; |
|
447
|
|
|
} |
|
448
|
|
|
|
|
449
|
|
|
/** |
|
450
|
|
|
* Return if the current action has been initialized and set. |
|
451
|
|
|
* |
|
452
|
|
|
* @return boolean |
|
453
|
|
|
*/ |
|
454
|
|
|
public function isCurrentActionDefined() |
|
455
|
|
|
{ |
|
456
|
|
|
return ($this->currentAction instanceof ActionInterface); |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
/** |
|
460
|
|
|
* Return admin configuration object. |
|
461
|
|
|
* |
|
462
|
|
|
* @return AdminConfiguration |
|
463
|
|
|
*/ |
|
464
|
|
|
public function getConfiguration() |
|
465
|
|
|
{ |
|
466
|
|
|
return $this->configuration; |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
|
|
/** |
|
470
|
|
|
* Return the filter form if it was initialized. |
|
471
|
|
|
* |
|
472
|
|
|
* @return FormInterface |
|
473
|
|
|
* |
|
474
|
|
|
* @throws Exception |
|
475
|
|
|
*/ |
|
476
|
|
|
public function getFilterForm() |
|
477
|
|
|
{ |
|
478
|
|
|
if (null === $this->filterForm) { |
|
479
|
|
|
throw new Exception( |
|
480
|
|
|
'The filter form is null. Check you have configured filters. You should initialize the filter form |
|
481
|
|
|
(with $admin->handleRequest() method for example)' |
|
482
|
|
|
); |
|
483
|
|
|
} |
|
484
|
|
|
|
|
485
|
|
|
return $this->filterForm; |
|
486
|
|
|
} |
|
487
|
|
|
|
|
488
|
|
|
/** |
|
489
|
|
|
* Return true if the filter form has been set. |
|
490
|
|
|
* |
|
491
|
|
|
* @return bool |
|
492
|
|
|
*/ |
|
493
|
|
|
public function hasFilterForm() |
|
494
|
|
|
{ |
|
495
|
|
|
return null !== $this->filterForm; |
|
496
|
|
|
} |
|
497
|
|
|
|
|
498
|
|
|
/** |
|
499
|
|
|
* Return a translation key for a message according to the Admin's translation pattern. |
|
500
|
|
|
* |
|
501
|
|
|
* @param string $message |
|
502
|
|
|
* @return string |
|
503
|
|
|
*/ |
|
504
|
|
|
protected function generateMessageTranslationKey($message) |
|
505
|
|
|
{ |
|
506
|
|
|
return $this->getTranslationKey( |
|
507
|
|
|
$this->configuration->getParameter('translation_pattern'), |
|
508
|
|
|
$message, |
|
509
|
|
|
$this->name |
|
510
|
|
|
); |
|
511
|
|
|
} |
|
512
|
|
|
} |
|
513
|
|
|
|
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.