Completed
Push — master ( 91fdab...75a7b9 )
by
unknown
13:37
created

AdminListController   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 512
Duplicated Lines 32.42 %

Coupling/Cohesion

Components 1
Dependencies 22

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 22
dl 166
loc 512
rs 4.5599
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getEntityManager() 0 4 1
A doIndexAction() 0 16 1
A doExportAction() 0 14 2
C doAddAction() 43 83 12
F doEditAction() 47 115 18
A doViewAction() 0 28 4
B doDeleteAction() 0 42 7
A doMoveUpAction() 32 32 3
A doMoveDownAction() 32 32 3
A getMaxSortableField() 0 9 1
A isLockableEntityLocked() 0 10 2
A setSortWeightOnNewItem() 0 11 2
A buildSortableFieldActions() 12 25 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AdminListController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdminListController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kunstmaan\AdminListBundle\Controller;
4
5
use Doctrine\ORM\EntityManager;
6
use Kunstmaan\AdminBundle\Entity\EntityInterface;
7
use Kunstmaan\AdminBundle\Event\AdaptSimpleFormEvent;
8
use Kunstmaan\AdminBundle\Event\Events;
9
use Kunstmaan\AdminBundle\FlashMessages\FlashTypes;
10
use Kunstmaan\AdminListBundle\AdminList\AdminList;
11
use Kunstmaan\AdminListBundle\AdminList\Configurator\AbstractAdminListConfigurator;
12
use Kunstmaan\AdminListBundle\AdminList\ItemAction\SimpleItemAction;
13
use Kunstmaan\AdminListBundle\AdminList\SortableInterface;
14
use Kunstmaan\AdminListBundle\Entity\LockableEntityInterface;
15
use Kunstmaan\AdminListBundle\Event\AdminListEvent;
16
use Kunstmaan\AdminListBundle\Event\AdminListEvents;
17
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
18
use Symfony\Component\HttpFoundation\RedirectResponse;
19
use Kunstmaan\AdminListBundle\Service\EntityVersionLockService;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
23
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
24
use Symfony\Component\PropertyAccess\PropertyAccess;
25
26
/**
27
 * AdminListController
28
 */
29
abstract class AdminListController extends Controller
30
{
31
    /**
32
     * You can override this method to return the correct entity manager when using multiple databases ...
33
     *
34
     * @return \Doctrine\Common\Persistence\ObjectManager|object
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use \Doctrine\Common\Persistence\ObjectManager.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
35
     */
36
    protected function getEntityManager()
37
    {
38
        return $this->getDoctrine()->getManager();
39
    }
40
41
    /**
42
     * Shows the list of entities
43
     *
44
     * @param AbstractAdminListConfigurator $configurator
45
     * @param null|Request $request
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $request a bit more specific; maybe use Request.
Loading history...
46
     *
47
     * @return Response
48
     */
49
    protected function doIndexAction(AbstractAdminListConfigurator $configurator, Request $request)
50
    {
51
        $em = $this->getEntityManager();
52
        /* @var AdminList $adminList */
53
        $adminList = $this->container->get("kunstmaan_adminlist.factory")->createList($configurator, $em);
54
        $adminList->bindRequest($request);
55
56
        $this->buildSortableFieldActions($configurator);
57
58
        return new Response(
59
            $this->renderView(
60
                $configurator->getListTemplate(),
61
                array('adminlist' => $adminList, 'adminlistconfigurator' => $configurator, 'addparams' => array())
62
            )
63
        );
64
    }
65
66
    /**
67
     * Export a list of Entities
68
     *
69
     * @param AbstractAdminListConfigurator $configurator The adminlist configurator
70
     * @param string $_format The format to export to
71
     * @param null|Request $request
72
     *
73
     * @throws AccessDeniedHttpException
74
     *
75
     * @return Response
76
     */
77
    protected function doExportAction(AbstractAdminListConfigurator $configurator, $_format, Request $request = null)
78
    {
79
        if (!$configurator->canExport()) {
80
            throw $this->createAccessDeniedException('You do not have sufficient rights to access this page.');
81
        }
82
83
        $em = $this->getEntityManager();
84
85
        /* @var AdminList $adminList */
86
        $adminList = $this->container->get("kunstmaan_adminlist.factory")->createExportList($configurator, $em);
87
        $adminList->bindRequest($request);
0 ignored issues
show
Bug introduced by
It seems like $request defined by parameter $request on line 77 can be null; however, Kunstmaan\AdminListBundl...dminList::bindRequest() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
88
89
        return $this->container->get("kunstmaan_adminlist.service.export")->getDownloadableResponse($adminList, $_format);
90
    }
91
92
    /**
93
     * Creates and processes the form to add a new Entity
94
     *
95
     * @param AbstractAdminListConfigurator $configurator The adminlist configurator
96
     * @param string $type The type to add
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
97
     * @param null|Request $request
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $request a bit more specific; maybe use Request.
Loading history...
98
     *
99
     * @throws AccessDeniedHttpException
100
     *
101
     * @return Response
102
     */
103
    protected function doAddAction(AbstractAdminListConfigurator $configurator, $type = null, Request $request)
104
    {
105
        if (!$configurator->canAdd()) {
106
            throw $this->createAccessDeniedException('You do not have sufficient rights to access this page.');
107
        }
108
109
        /* @var EntityManager $em */
110
        $em = $this->getEntityManager();
111
        $entityName = null;
0 ignored issues
show
Unused Code introduced by
$entityName is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
112
        $entityName = (isset($type)) ? $type : $configurator->getRepositoryName();
113
114
        $classMetaData = $em->getClassMetadata($entityName);
115
        // Creates a new instance of the mapped class, without invoking the constructor.
116
        $classname = $classMetaData->getName();
117
        $helper = new $classname();
118
        $helper = $configurator->decorateNewEntity($helper);
119
120
        $formType = $configurator->getAdminType($helper);
121
122
        $event = new AdaptSimpleFormEvent($request, $formType, $helper, $configurator->getAdminTypeOptions());
123
        $event = $this->container->get('event_dispatcher')->dispatch(Events::ADAPT_SIMPLE_FORM, $event);
124
        $tabPane = $event->getTabPane();
125
126
        $form = $this->createForm($formType, $helper, $event->getOptions());
127
128 View Code Duplication
        if ($request->isMethod('POST')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
129
            if ($tabPane) {
130
                $tabPane->bindRequest($request);
131
                $form = $tabPane->getForm();
132
            } else {
133
                $form->handleRequest($request);
134
            }
135
136
            // Don't redirect to listing when coming from ajax request, needed for url chooser.
137
            if ($form->isSubmitted() && $form->isValid() && !$request->isXmlHttpRequest()) {
138
                $adminListEvent = new AdminListEvent($helper, $request, $form);
139
                $this->container->get('event_dispatcher')->dispatch(
140
                    AdminListEvents::PRE_ADD,
141
                    $adminListEvent
142
                );
143
144
                // Check if Response is given
145
                if ($adminListEvent->getResponse() instanceof Response) {
146
                    return $adminListEvent->getResponse();
147
                }
148
149
                // Set sort weight
150
                $helper = $this->setSortWeightOnNewItem($configurator, $helper);
151
152
                $em->persist($helper);
153
                $em->flush();
154
                $this->container->get('event_dispatcher')->dispatch(
155
                    AdminListEvents::POST_ADD,
156
                    $adminListEvent
157
                );
158
159
                // Check if Response is given
160
                if ($adminListEvent->getResponse() instanceof Response) {
161
                    return $adminListEvent->getResponse();
162
                }
163
164
                $indexUrl = $configurator->getIndexUrl();
165
166
                return new RedirectResponse(
167
                    $this->generateUrl($indexUrl['path'], isset($indexUrl['params']) ? $indexUrl['params'] : array())
168
                );
169
            }
170
        }
171
172
        $params = [
173
            'form' => $form->createView(),
174
            'adminlistconfigurator' => $configurator,
175
            'entityVersionLockCheck' => false
176
        ];
177
178
        if ($tabPane) {
179
            $params = array_merge($params, array('tabPane' => $tabPane));
180
        }
181
182
        return new Response(
183
            $this->renderView($configurator->getAddTemplate(), $params)
184
        );
185
    }
186
187
    /**
188
     * Creates and processes the edit form for an Entity using its ID
189
     *
190
     * @param AbstractAdminListConfigurator $configurator The adminlist configurator
191
     * @param string $entityId The id of the entity that will be edited
192
     * @param null|Request $request
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $request a bit more specific; maybe use Request.
Loading history...
193
     *
194
     * @throws NotFoundHttpException
195
     * @throws AccessDeniedHttpException
196
     *
197
     * @return Response
198
     */
199
    protected function doEditAction(AbstractAdminListConfigurator $configurator, $entityId, Request $request)
200
    {
201
        /* @var EntityManager $em */
202
        $em = $this->getEntityManager();
203
        $helper = $em->getRepository($configurator->getRepositoryName())->findOneById($entityId);
0 ignored issues
show
Bug introduced by
The method findOneById() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findOneBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
204
205
        if ($helper === null) {
206
            throw new NotFoundHttpException("Entity not found.");
207
        }
208
209
        if (!$configurator->canEdit($helper)) {
210
            throw $this->createAccessDeniedException('You do not have sufficient rights to access this page.');
211
        }
212
213
        if ($helper instanceof LockableEntityInterface) {
214
            // This entity is locked
215
            if ($this->isLockableEntityLocked($helper)) {
216
                $indexUrl = $configurator->getIndexUrl();
217
                // Don't redirect to listing when coming from ajax request, needed for url chooser.
218
                if (!$request->isXmlHttpRequest()) {
219
                    /** @var EntityVersionLockService $entityVersionLockService*/
220
                    $entityVersionLockService = $this->container->get('kunstmaan_entity.admin_entity.entity_version_lock_service');
221
222
                    $user = $entityVersionLockService->getUsersWithEntityVersionLock($helper, $this->getUser());
223
                    $message = $this->container->get('translator')->trans('kuma_admin_list.edit.flash.locked', array('%user%' => implode(', ', $user)));
224
                    $this->addFlash(
225
                        FlashTypes::WARNING,
226
                        $message
227
                    );
228
                    return new RedirectResponse(
229
                        $this->generateUrl(
230
                            $indexUrl['path'],
231
                            isset($indexUrl['params']) ? $indexUrl['params'] : array()
232
                        )
233
                    );
234
                }
235
            }
236
        }
237
238
        $formType = $configurator->getAdminType($helper);
239
240
        $event = new AdaptSimpleFormEvent($request, $formType, $helper, $configurator->getAdminTypeOptions());
241
        $event = $this->container->get('event_dispatcher')->dispatch(Events::ADAPT_SIMPLE_FORM, $event);
242
        $tabPane = $event->getTabPane();
243
244
        $form = $this->createForm($formType, $helper, $event->getOptions());
245
246 View Code Duplication
        if ($request->isMethod('POST')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
247
248
            if ($tabPane) {
249
                $tabPane->bindRequest($request);
250
                $form = $tabPane->getForm();
251
            } else {
252
                $form->handleRequest($request);
253
            }
254
255
            // Don't redirect to listing when coming from ajax request, needed for url chooser.
256
            if ($form->isSubmitted() && $form->isValid() && !$request->isXmlHttpRequest()) {
257
                $adminListEvent = new AdminListEvent($helper, $request, $form);
258
                $this->container->get('event_dispatcher')->dispatch(
259
                    AdminListEvents::PRE_EDIT,
260
                    $adminListEvent
261
                );
262
263
                // Check if Response is given
264
                if ($adminListEvent->getResponse() instanceof Response) {
265
                    return $adminListEvent->getResponse();
266
                }
267
268
                $em->persist($helper);
269
                $em->flush();
270
                $this->container->get('event_dispatcher')->dispatch(
271
                    AdminListEvents::POST_EDIT,
272
                    $adminListEvent
273
                );
274
275
                // Check if Response is given
276
                if ($adminListEvent->getResponse() instanceof Response) {
277
                    return $adminListEvent->getResponse();
278
                }
279
280
                $indexUrl = $configurator->getIndexUrl();
281
282
                // Don't redirect to listing when coming from ajax request, needed for url chooser.
283
                if (!$request->isXmlHttpRequest()) {
284
                    return new RedirectResponse(
285
                        $this->generateUrl(
286
                            $indexUrl['path'],
287
                            isset($indexUrl['params']) ? $indexUrl['params'] : array()
288
                        )
289
                    );
290
                }
291
            }
292
        }
293
294
        $configurator->buildItemActions();
295
296
        $params = [
297
            'form' => $form->createView(),
298
            'entity' => $helper, 'adminlistconfigurator' => $configurator,
299
            'entityVersionLockInterval' => $this->container->getParameter('kunstmaan_entity.lock_check_interval'),
300
            'entityVersionLockCheck' => $this->container->getParameter('kunstmaan_entity.lock_enabled') && $helper instanceof LockableEntityInterface,
301
        ];
302
303
        if ($tabPane) {
304
            $params = array_merge($params, array('tabPane' => $tabPane));
305
        }
306
307
        return new Response(
308
            $this->renderView(
309
                $configurator->getEditTemplate(),
310
                $params
311
            )
312
        );
313
    }
314
315
    protected function doViewAction(AbstractAdminListConfigurator $configurator, $entityId, Request $request)
316
    {
317
        /* @var EntityManager $em */
318
        $em = $this->getEntityManager();
319
        $helper = $em->getRepository($configurator->getRepositoryName())->findOneById($entityId);
0 ignored issues
show
Bug introduced by
The method findOneById() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findOneBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
320
        if ($helper === null) {
321
            throw new NotFoundHttpException("Entity not found.");
322
        }
323
324
        if (!$configurator->canView($helper)) {
325
            throw $this->createAccessDeniedException('You do not have sufficient rights to access this page.');
326
        }
327
328
        $MetaData = $em->getClassMetadata($configurator->getRepositoryName());
329
        $fields = array();
330
        $accessor = PropertyAccess::createPropertyAccessor();
331
        foreach ($MetaData->fieldNames as $value) {
332
            $fields[$value] = $accessor->getValue($helper, $value);
333
        }
334
335
336
        return new Response(
337
            $this->renderView(
338
                $configurator->getViewTemplate(),
339
                array('entity' => $helper, 'adminlistconfigurator' => $configurator, 'fields' => $fields)
340
            )
341
        );
342
    }
343
344
    /**
345
     * Delete the Entity using its ID
346
     *
347
     * @param AbstractAdminListConfigurator $configurator The adminlist configurator
348
     * @param integer $entityId The id to delete
349
     * @param null|Request $request
0 ignored issues
show
Documentation introduced by
Consider making the type for parameter $request a bit more specific; maybe use Request.
Loading history...
350
     *
351
     * @throws NotFoundHttpException
352
     * @throws AccessDeniedHttpException
353
     *
354
     * @return Response
355
     */
356
    protected function doDeleteAction(AbstractAdminListConfigurator $configurator, $entityId, Request $request)
357
    {
358
        /* @var $em EntityManager */
359
        $em = $this->getEntityManager();
360
        $helper = $em->getRepository($configurator->getRepositoryName())->findOneById($entityId);
0 ignored issues
show
Bug introduced by
The method findOneById() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findOneBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
361
        if ($helper === null) {
362
            throw new NotFoundHttpException("Entity not found.");
363
        }
364
        if (!$configurator->canDelete($helper)) {
365
            throw $this->createAccessDeniedException('You do not have sufficient rights to access this page.');
366
        }
367
368
        $indexUrl = $configurator->getIndexUrl();
369
        if ($request->isMethod('POST')) {
370
            $adminListEvent = new AdminListEvent($helper, $request);
371
            $this->container->get('event_dispatcher')->dispatch(
372
                AdminListEvents::PRE_DELETE,
373
                $adminListEvent
374
            );
375
376
            // Check if Response is given
377
            if ($adminListEvent->getResponse() instanceof Response) {
378
                return $adminListEvent->getResponse();
379
            }
380
381
            $em->remove($helper);
382
            $em->flush();
383
            $this->container->get('event_dispatcher')->dispatch(
384
                AdminListEvents::POST_DELETE,
385
                $adminListEvent
386
            );
387
388
            // Check if Response is given
389
            if ($adminListEvent->getResponse() instanceof Response) {
390
                return $adminListEvent->getResponse();
391
            }
392
        }
393
394
        return new RedirectResponse(
395
            $this->generateUrl($indexUrl['path'], isset($indexUrl['params']) ? $indexUrl['params'] : array())
396
        );
397
    }
398
399
    /**
400
     * Move an item up in the list.
401
     *
402
     * @return RedirectResponse
403
     */
404 View Code Duplication
    protected function doMoveUpAction(AbstractAdminListConfigurator $configurator, $entityId, Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
405
    {
406
        $em = $this->getEntityManager();
407
        $sortableField = $configurator->getSortableField();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Kunstmaan\AdminListBundl...ctAdminListConfigurator as the method getSortableField() does only exist in the following sub-classes of Kunstmaan\AdminListBundl...ctAdminListConfigurator: Kunstmaan\AdminListBundl...nList\Configurator\DBAL, Kunstmaan\AdminListBundl...inList\Configurator\ORM. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
408
        $repo = $em->getRepository($configurator->getRepositoryName());
409
        $item = $repo->find($entityId);
410
411
        $setter = "set".ucfirst($sortableField);
412
        $getter = "get".ucfirst($sortableField);
413
414
        $nextItem = $repo->createQueryBuilder('i')
415
            ->where('i.'.$sortableField.' < :weight')
416
            ->setParameter('weight', $item->$getter())
417
            ->orderBy('i.'.$sortableField, 'DESC')
418
            ->setMaxResults(1)
419
            ->getQuery()
420
            ->getOneOrNullResult();
421
        if ($nextItem) {
422
            $nextItem->$setter($item->$getter());
423
            $em->persist($nextItem);
424
            $item->$setter($item->$getter() - 1);
425
426
            $em->persist($item);
0 ignored issues
show
Bug introduced by
It seems like $item defined by $repo->find($entityId) on line 409 can also be of type null; however, Doctrine\Common\Persiste...bjectManager::persist() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
427
            $em->flush();
428
        }
429
430
        $indexUrl = $configurator->getIndexUrl();
431
432
        return new RedirectResponse(
433
            $this->generateUrl($indexUrl['path'], isset($indexUrl['params']) ? $indexUrl['params'] : array())
434
        );
435
    }
436
437 View Code Duplication
    protected function doMoveDownAction(AbstractAdminListConfigurator $configurator, $entityId, Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
438
    {
439
        $em = $this->getEntityManager();
440
        $sortableField = $configurator->getSortableField();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Kunstmaan\AdminListBundl...ctAdminListConfigurator as the method getSortableField() does only exist in the following sub-classes of Kunstmaan\AdminListBundl...ctAdminListConfigurator: Kunstmaan\AdminListBundl...nList\Configurator\DBAL, Kunstmaan\AdminListBundl...inList\Configurator\ORM. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
441
        $repo = $em->getRepository($configurator->getRepositoryName());
442
        $item = $repo->find($entityId);
443
444
        $setter = "set".ucfirst($sortableField);
445
        $getter = "get".ucfirst($sortableField);
446
447
        $nextItem = $repo->createQueryBuilder('i')
448
            ->where('i.'.$sortableField.' > :weight')
449
            ->setParameter('weight', $item->$getter())
450
            ->orderBy('i.'.$sortableField, 'ASC')
451
            ->setMaxResults(1)
452
            ->getQuery()
453
            ->getOneOrNullResult();
454
        if ($nextItem) {
455
            $nextItem->$setter($item->$getter());
456
            $em->persist($nextItem);
457
            $item->$setter($item->$getter() + 1);
458
459
            $em->persist($item);
0 ignored issues
show
Bug introduced by
It seems like $item defined by $repo->find($entityId) on line 442 can also be of type null; however, Doctrine\Common\Persiste...bjectManager::persist() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
460
            $em->flush();
461
        }
462
463
        $indexUrl = $configurator->getIndexUrl();
464
465
        return new RedirectResponse(
466
            $this->generateUrl($indexUrl['path'], isset($indexUrl['params']) ? $indexUrl['params'] : array())
467
        );
468
    }
469
470
    private function getMaxSortableField($repo, $sort)
471
    {
472
        $maxWeight = $repo->createQueryBuilder('i')
473
            ->select('max(i.'.$sort.')')
474
            ->getQuery()
475
            ->getSingleScalarResult();
476
477
        return (int)$maxWeight;
478
    }
479
480
    /**
481
     * @param LockableEntityInterface $entity
482
     * @return bool
483
     */
484
    protected function isLockableEntityLocked(LockableEntityInterface $entity)
485
    {
486
        /** @var EntityVersionLockService $entityVersionLockService */
487
        $entityVersionLockService = $this->container->get('kunstmaan_entity.admin_entity.entity_version_lock_service');
488
489
        return $entityVersionLockService->isEntityBelowThreshold($entity) && $entityVersionLockService->isEntityLocked(
490
                $this->getUser(),
0 ignored issues
show
Documentation introduced by
$this->getUser() is of type null|object, but the function expects a object<FOS\UserBundle\Model\User>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
491
                $entity
492
            );
493
    }
494
495
    /**
496
     * Sets the sort weight on a new item. Can be overridden if a non-default sorting implementation is being used.
497
     *
498
     * @param AbstractAdminListConfigurator $configurator The adminlist configurator
499
     * @param $item
500
     *
501
     * @return mixed
502
     */
503
    protected function setSortWeightOnNewItem(AbstractAdminListConfigurator $configurator, $item) {
504
        if ($configurator instanceof SortableInterface) {
505
            $repo = $this->getEntityManager()->getRepository($configurator->getRepositoryName());
506
            $sort = $configurator->getSortableField();
507
            $weight = $this->getMaxSortableField($repo, $sort);
508
            $setter = "set".ucfirst($sort);
509
            $item->$setter($weight + 1);
510
        }
511
512
        return $item;
513
    }
514
515
    protected function buildSortableFieldActions(AbstractAdminListConfigurator $configurator)
516
    {
517
        // Check if Sortable interface is implemented
518
        if ($configurator instanceof SortableInterface) {
519 View Code Duplication
            $route = function (EntityInterface $item) use ($configurator) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
520
                return array(
521
                    'path' => $configurator->getPathByConvention().'_move_up',
522
                    'params' => array('id' => $item->getId()),
523
                );
524
            };
525
526
            $action = new SimpleItemAction($route, 'arrow-up', 'kuma_admin_list.action.move_up');
527
            $configurator->addItemAction($action);
528
529 View Code Duplication
            $route = function (EntityInterface $item) use ($configurator) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
530
                return array(
531
                    'path' => $configurator->getPathByConvention().'_move_down',
532
                    'params' => array('id' => $item->getId()),
533
                );
534
            };
535
536
            $action = new SimpleItemAction($route, 'arrow-down', 'kuma_admin_list.action.move_down');
537
            $configurator->addItemAction($action);
538
        }
539
    }
540
}
541