Completed
Push — master ( a7f5a2...7520cc )
by Craig
06:59
created

AbstractEditHandler   D

Complexity

Total Complexity 80

Size/Duplication

Total Lines 611
Duplicated Lines 6.38 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 39
loc 611
rs 4.828
c 0
b 0
f 0
wmc 80
lcom 1
cbo 6

15 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 27 1
A setTranslator() 0 4 1
A createForm() 0 5 1
A getTemplateParameters() 0 4 1
A createCompositeIdentifier() 0 16 4
A initEntityForEditing() 0 4 1
B initEntityForCreation() 0 33 6
A getRedirectCodes() 0 8 1
C handleCommand() 5 37 13
C getDefaultMessage() 21 29 7
A addDefaultMessage() 0 16 4
C fetchInputData() 6 25 11
A applyAction() 0 5 1
A setLockingApi() 0 4 1
F processForm() 7 111 27

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 AbstractEditHandler 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 AbstractEditHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Routes.
4
 *
5
 * @copyright Zikula contributors (Zikula)
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 * @author Zikula contributors <[email protected]>.
8
 * @link http://www.zikula.org
9
 * @link http://zikula.org
10
 * @version Generated by ModuleStudio 0.7.4 (http://modulestudio.de).
11
 */
12
13
namespace Zikula\RoutesModule\Form\Handler\Common\Base;
14
15
use Psr\Log\LoggerInterface;
16
use RuntimeException;
17
use Symfony\Component\Form\AbstractType;
18
use Symfony\Component\Form\FormFactoryInterface;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\RequestStack;
22
use Symfony\Component\Routing\RouterInterface;
23
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
24
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
25
use Zikula\Common\Translator\TranslatorInterface;
26
use Zikula\Common\Translator\TranslatorTrait;
27
use Zikula\Core\Doctrine\EntityAccess;
28
use Zikula\PageLockModule\Api\ApiInterface\LockingApiInterface;
29
use Zikula\PermissionsModule\Api\ApiInterface\PermissionApiInterface;
30
use Zikula\UsersModule\Api\ApiInterface\CurrentUserApiInterface;
31
use Zikula\RoutesModule\Entity\Factory\RoutesFactory;
32
use Zikula\RoutesModule\Helper\ControllerHelper;
33
use Zikula\RoutesModule\Helper\ModelHelper;
34
use Zikula\RoutesModule\Helper\WorkflowHelper;
35
36
/**
37
 * This handler class handles the page events of editing forms.
38
 * It collects common functionality required by different object types.
39
 */
40
abstract class AbstractEditHandler
41
{
42
    use TranslatorTrait;
43
44
    /**
45
     * Name of treated object type.
46
     *
47
     * @var string
48
     */
49
    protected $objectType;
50
51
    /**
52
     * Name of treated object type starting with upper case.
53
     *
54
     * @var string
55
     */
56
    protected $objectTypeCapital;
57
58
    /**
59
     * Lower case version.
60
     *
61
     * @var string
62
     */
63
    protected $objectTypeLower;
64
65
    /**
66
     * Permission component based on object type.
67
     *
68
     * @var string
69
     */
70
    protected $permissionComponent;
71
72
    /**
73
     * Reference to treated entity instance.
74
     *
75
     * @var EntityAccess
76
     */
77
    protected $entityRef = null;
78
79
    /**
80
     * List of identifier names.
81
     *
82
     * @var array
83
     */
84
    protected $idFields = [];
85
86
    /**
87
     * List of identifiers of treated entity.
88
     *
89
     * @var array
90
     */
91
    protected $idValues = [];
92
93
    /**
94
     * Code defining the redirect goal after command handling.
95
     *
96
     * @var string
97
     */
98
    protected $returnTo = null;
99
100
    /**
101
     * Whether a create action is going to be repeated or not.
102
     *
103
     * @var boolean
104
     */
105
    protected $repeatCreateAction = false;
106
107
    /**
108
     * Url of current form with all parameters for multiple creations.
109
     *
110
     * @var string
111
     */
112
    protected $repeatReturnUrl = null;
113
114
    /**
115
     * Whether an existing item is used as template for a new one.
116
     *
117
     * @var boolean
118
     */
119
    protected $hasTemplateId = false;
120
121
    /**
122
     * Whether the PageLock extension is used for this entity type or not.
123
     *
124
     * @var boolean
125
     */
126
    protected $hasPageLockSupport = false;
127
128
    /**
129
     * @var ZikulaHttpKernelInterface
130
     */
131
    protected $kernel;
132
133
    /**
134
     * @var FormFactoryInterface
135
     */
136
    protected $formFactory;
137
138
    /**
139
     * The current request.
140
     *
141
     * @var Request
142
     */
143
    protected $request;
144
145
    /**
146
     * The router.
147
     *
148
     * @var RouterInterface
149
     */
150
    protected $router;
151
152
    /**
153
     * @var LoggerInterface
154
     */
155
    protected $logger;
156
157
    /**
158
     * @var PermissionApiInterface
159
     */
160
    protected $permissionApi;
161
162
    /**
163
     * @var CurrentUserApiInterface
164
     */
165
    protected $currentUserApi;
166
167
    /**
168
     * @var RoutesFactory
169
     */
170
    protected $entityFactory;
171
172
    /**
173
     * @var ControllerHelper
174
     */
175
    protected $controllerHelper;
176
177
    /**
178
     * @var ModelHelper
179
     */
180
    protected $modelHelper;
181
182
    /**
183
     * @var WorkflowHelper
184
     */
185
    protected $workflowHelper;
186
187
    /**
188
     * Reference to optional locking api.
189
     *
190
     * @var LockingApiInterface
191
     */
192
    protected $lockingApi = null;
193
194
    /**
195
     * The handled form type.
196
     *
197
     * @var AbstractType
198
     */
199
    protected $form;
200
201
    /**
202
     * Template parameters.
203
     *
204
     * @var array
205
     */
206
    protected $templateParameters = [];
207
208
    /**
209
     * EditHandler constructor.
210
     *
211
     * @param ZikulaHttpKernelInterface $kernel           Kernel service instance
212
     * @param TranslatorInterface       $translator       Translator service instance
213
     * @param FormFactoryInterface      $formFactory      FormFactory service instance
214
     * @param RequestStack              $requestStack     RequestStack service instance
215
     * @param RouterInterface           $router           Router service instance
216
     * @param LoggerInterface           $logger           Logger service instance
217
     * @param PermissionApiInterface             $permissionApi    PermissionApi service instance
218
     * @param CurrentUserApiInterface   $currentUserApi   CurrentUserApi service instance
219
     * @param RoutesFactory $entityFactory RoutesFactory service instance
220
     * @param ControllerHelper          $controllerHelper ControllerHelper service instance
221
     * @param ModelHelper               $modelHelper      ModelHelper service instance
222
     * @param WorkflowHelper            $workflowHelper   WorkflowHelper service instance
223
     */
224
    public function __construct(
225
        ZikulaHttpKernelInterface $kernel,
226
        TranslatorInterface $translator,
227
        FormFactoryInterface $formFactory,
228
        RequestStack $requestStack,
229
        RouterInterface $router,
230
        LoggerInterface $logger,
231
        PermissionApiInterface $permissionApi,
232
        CurrentUserApiInterface $currentUserApi,
233
        RoutesFactory $entityFactory,
234
        ControllerHelper $controllerHelper,
235
        ModelHelper $modelHelper,
236
        WorkflowHelper $workflowHelper
237
    ) {
238
        $this->kernel = $kernel;
239
        $this->setTranslator($translator);
240
        $this->formFactory = $formFactory;
241
        $this->request = $requestStack->getCurrentRequest();
242
        $this->router = $router;
243
        $this->logger = $logger;
244
        $this->permissionApi = $permissionApi;
245
        $this->currentUserApi = $currentUserApi;
246
        $this->entityFactory = $entityFactory;
247
        $this->controllerHelper = $controllerHelper;
248
        $this->modelHelper = $modelHelper;
249
        $this->workflowHelper = $workflowHelper;
250
    }
251
252
    /**
253
     * Sets the translator.
254
     *
255
     * @param TranslatorInterface $translator Translator service instance
256
     */
257
    public function setTranslator(/*TranslatorInterface */$translator)
258
    {
259
        $this->translator = $translator;
260
    }
261
262
    /**
263
     * Initialise form handler.
264
     *
265
     * This method takes care of all necessary initialisation of our data and form states.
266
     *
267
     * @param array $templateParameters List of preassigned template variables
268
     *
269
     * @return boolean False in case of initialisation errors, otherwise true
270
     *
271
     * @throws RuntimeException Thrown if the workflow actions can not be determined
272
     */
273
    public function processForm(array $templateParameters)
274
    {
275
        $this->templateParameters = $templateParameters;
276
    
277
        // initialise redirect goal
278
        $this->returnTo = $this->request->query->get('returnTo', null);
279
        // default to referer
280
        $refererSessionVar = 'zikularoutesmodule' . $this->objectTypeCapital . 'Referer';
281
        if (null === $this->returnTo && $this->request->headers->has('referer')) {
282
            $currentReferer = $this->request->headers->get('referer');
283 View Code Duplication
            if ($currentReferer != $this->request->getUri()) {
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...
284
                $this->returnTo = $currentReferer;
285
                $this->request->getSession()->set($refererSessionVar, $this->returnTo);
286
            }
287
        }
288 View Code Duplication
        if (null === $this->returnTo && $this->request->getSession()->has($refererSessionVar)) {
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...
289
            $this->returnTo = $this->request->getSession()->get($refererSessionVar);
290
        }
291
        // store current uri for repeated creations
292
        $this->repeatReturnUrl = $this->request->getSchemeAndHttpHost() . $this->request->getBasePath() . $this->request->getPathInfo();
293
    
294
        $this->permissionComponent = 'ZikulaRoutesModule:' . $this->objectTypeCapital . ':';
295
    
296
        $this->idFields = $this->entityFactory->getIdFields($this->objectType);
297
    
298
        // retrieve identifier of the object we wish to edit
299
        $this->idValues = $this->controllerHelper->retrieveIdentifier($this->request, [], $this->objectType);
300
        $hasIdentifier = $this->controllerHelper->isValidIdentifier($this->idValues);
301
    
302
        $entity = null;
0 ignored issues
show
Unused Code introduced by
$entity 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...
303
        $this->templateParameters['mode'] = $hasIdentifier ? 'edit' : 'create';
304
    
305
        if ($this->templateParameters['mode'] == 'edit') {
306
            if (!$this->permissionApi->hasPermission($this->permissionComponent, $this->createCompositeIdentifier() . '::', ACCESS_EDIT)) {
307
                throw new AccessDeniedException();
308
            }
309
    
310
            $entity = $this->initEntityForEditing();
311
            if (null !== $entity) {
312
                if (true === $this->hasPageLockSupport && $this->kernel->isBundle('ZikulaPageLockModule') && null !== $this->lockingApi) {
313
                    // try to guarantee that only one person at a time can be editing this entity
314
                    $lockName = 'ZikulaRoutesModule' . $this->objectTypeCapital . $this->createCompositeIdentifier();
315
                    $this->lockingApi->addLock($lockName, $this->getRedirectUrl(null));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\RoutesModule\Form...ase\AbstractEditHandler as the method getRedirectUrl() does only exist in the following sub-classes of Zikula\RoutesModule\Form...ase\AbstractEditHandler: Zikula\RoutesModule\Form...ase\AbstractEditHandler, Zikula\RoutesModule\Form\Handler\Route\EditHandler. 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...
316
                }
317
            }
318
        } else {
319
            if (!$this->permissionApi->hasPermission($this->permissionComponent, '::', ACCESS_EDIT)) {
320
                throw new AccessDeniedException();
321
            }
322
    
323
            $entity = $this->initEntityForCreation();
324
    
325
            // set default values from request parameters
326
            foreach ($this->request->query->all() as $key => $value) {
327
                if (strlen($key) < 5 || substr($key, 0, 4) != 'set_') {
328
                    continue;
329
                }
330
                $fieldName = str_replace('set_', '', $key);
331
                $setterName = 'set' . ucfirst($fieldName);
332
                if (!method_exists($entity, $setterName)) {
333
                    continue;
334
                }
335
                $entity[$fieldName] = $value;
336
            }
337
        }
338
    
339
        if (null === $entity) {
340
            $this->request->getSession()->getFlashBag()->add('error', $this->__('No such item found.'));
341
    
342
            return new RedirectResponse($this->getRedirectUrl(['commandName' => 'cancel']), 302);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\RoutesModule\Form...ase\AbstractEditHandler as the method getRedirectUrl() does only exist in the following sub-classes of Zikula\RoutesModule\Form...ase\AbstractEditHandler: Zikula\RoutesModule\Form...ase\AbstractEditHandler, Zikula\RoutesModule\Form\Handler\Route\EditHandler. 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...
343
        }
344
    
345
        // save entity reference for later reuse
346
        $this->entityRef = $entity;
347
    
348
    
349
        $actions = $this->workflowHelper->getActionsForObject($entity);
350
        if (false === $actions || !is_array($actions)) {
351
            $this->request->getSession()->getFlashBag()->add('error', $this->__('Error! Could not determine workflow actions.'));
352
            $logArgs = ['app' => 'ZikulaRoutesModule', 'user' => $this->currentUserApi->get('uname'), 'entity' => $this->objectType, 'id' => $entity->createCompositeIdentifier()];
353
            $this->logger->error('{app}: User {user} tried to edit the {entity} with id {id}, but failed to determine available workflow actions.', $logArgs);
354
            throw new \RuntimeException($this->__('Error! Could not determine workflow actions.'));
355
        }
356
    
357
        $this->templateParameters['actions'] = $actions;
358
    
359
        $this->form = $this->createForm();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $this->form is correct as $this->createForm() (which targets Zikula\RoutesModule\Form...itHandler::createForm()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
360
        if (!is_object($this->form)) {
361
            return false;
362
        }
363
    
364
        // handle form request and check validity constraints of edited entity
365
        if ($this->form->handleRequest($this->request) && $this->form->isSubmitted()) {
366
            if ($this->form->isValid()) {
367
                $result = $this->handleCommand();
368
                if (false === $result) {
369
                    $this->templateParameters['form'] = $this->form->createView();
370
                }
371
    
372
                return $result;
373
            }
374
            if ($this->form->get('cancel')->isClicked()) {
375
                return new RedirectResponse($this->getRedirectUrl(['commandName' => 'cancel']), 302);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\RoutesModule\Form...ase\AbstractEditHandler as the method getRedirectUrl() does only exist in the following sub-classes of Zikula\RoutesModule\Form...ase\AbstractEditHandler: Zikula\RoutesModule\Form...ase\AbstractEditHandler, Zikula\RoutesModule\Form\Handler\Route\EditHandler. 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...
376
            }
377
        }
378
    
379
        $this->templateParameters['form'] = $this->form->createView();
380
    
381
        // everything okay, no initialisation errors occured
382
        return true;
383
    }
384
    
385
    /**
386
     * Creates the form type.
387
     */
388
    protected function createForm()
389
    {
390
        // to be customised in sub classes
391
        return null;
392
    }
393
    
394
    /**
395
     * Returns the template parameters.
396
     *
397
     * @return array
398
     */
399
    public function getTemplateParameters()
400
    {
401
        return $this->templateParameters;
402
    }
403
    
404
    /**
405
     * Create concatenated identifier string (for composite keys).
406
     *
407
     * @return String concatenated identifiers
408
     */
409
    protected function createCompositeIdentifier()
410
    {
411
        $itemId = '';
412
        if ($this->templateParameters['mode'] == 'create') {
413
            return $itemId;
414
        }
415
    
416
        foreach ($this->idFields as $idField) {
417
            if (!empty($itemId)) {
418
                $itemId .= '_';
419
            }
420
            $itemId .= $this->idValues[$idField];
421
        }
422
    
423
        return $itemId;
424
    }
425
    
426
    /**
427
     * Initialise existing entity for editing.
428
     *
429
     * @return EntityAccess|null Desired entity instance or null
430
     */
431
    protected function initEntityForEditing()
432
    {
433
        return $this->entityFactory->getRepository($this->objectType)->selectById($this->idValues);
434
    }
435
    
436
    /**
437
     * Initialise new entity for creation.
438
     *
439
     * @return EntityAccess|null Desired entity instance or null
440
     */
441
    protected function initEntityForCreation()
442
    {
443
        $this->hasTemplateId = false;
444
        $templateId = $this->request->query->get('astemplate', '');
445
        $entity = null;
446
    
447
        if (!empty($templateId)) {
448
            $templateIdValueParts = explode('_', $templateId);
449
            $this->hasTemplateId = count($templateIdValueParts) == count($this->idFields);
450
    
451
            if (true === $this->hasTemplateId) {
452
                $templateIdValues = [];
453
                $i = 0;
454
                foreach ($this->idFields as $idField) {
455
                    $templateIdValues[$idField] = $templateIdValueParts[$i];
456
                    $i++;
457
                }
458
                // reuse existing entity
459
                $entityT = $this->entityFactory->getRepository($this->objectType)->selectById($templateIdValues);
460
                if (null === $entityT) {
461
                    return null;
462
                }
463
                $entity = clone $entityT;
464
            }
465
        }
466
    
467
        if (null === $entity) {
468
            $createMethod = 'create' . ucfirst($this->objectType);
469
            $entity = $this->entityFactory->$createMethod();
470
        }
471
    
472
        return $entity;
473
    }
474
475
    /**
476
     * Get list of allowed redirect codes.
477
     *
478
     * @return array list of possible redirect codes
479
     */
480
    protected function getRedirectCodes()
481
    {
482
        $codes = [];
483
    
484
        // to be filled by subclasses
485
    
486
        return $codes;
487
    }
488
489
    /**
490
     * Command event handler.
491
     *
492
     * @param array $args List of arguments
493
     *
494
     * @return mixed Redirect or false on errors
495
     */
496
    public function handleCommand($args = [])
497
    {
498
        // build $args for BC (e.g. used by redirect handling)
499 View Code Duplication
        foreach ($this->templateParameters['actions'] as $action) {
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...
500
            if ($this->form->get($action['id'])->isClicked()) {
501
                $args['commandName'] = $action['id'];
502
            }
503
        }
504
        if ($this->form->get('cancel')->isClicked()) {
505
            $args['commandName'] = 'cancel';
506
        }
507
    
508
        $action = $args['commandName'];
509
        $isRegularAction = !in_array($action, ['delete', 'cancel']);
510
    
511
        if ($isRegularAction || $action == 'delete') {
512
            $this->fetchInputData($args);
513
        }
514
    
515
        // get treated entity reference from persisted member var
516
        $entity = $this->entityRef;
0 ignored issues
show
Unused Code introduced by
$entity 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...
517
    
518
        if ($isRegularAction || $action == 'delete') {
519
            $success = $this->applyAction($args);
520
            if (!$success) {
521
                // the workflow operation failed
522
                return false;
523
            }
524
        }
525
    
526
        if (true === $this->hasPageLockSupport && $this->templateParameters['mode'] == 'edit' && $this->kernel->isBundle('ZikulaPageLockModule') && null !== $this->lockingApi) {
527
            $lockName = 'ZikulaRoutesModule' . $this->objectTypeCapital . $this->createCompositeIdentifier();
528
            $this->lockingApi->releaseLock($lockName);
529
        }
530
    
531
        return new RedirectResponse($this->getRedirectUrl($args), 302);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\RoutesModule\Form...ase\AbstractEditHandler as the method getRedirectUrl() does only exist in the following sub-classes of Zikula\RoutesModule\Form...ase\AbstractEditHandler: Zikula\RoutesModule\Form...ase\AbstractEditHandler, Zikula\RoutesModule\Form\Handler\Route\EditHandler. 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...
532
    }
533
    
534
    /**
535
     * Get success or error message for default operations.
536
     *
537
     * @param array   $args    arguments from handleCommand method
538
     * @param Boolean $success true if this is a success, false for default error
539
     *
540
     * @return String desired status or error message
541
     */
542
    protected function getDefaultMessage($args, $success = false)
543
    {
544
        $message = '';
545
        switch ($args['commandName']) {
546 View Code Duplication
            case 'create':
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...
547
                if (true === $success) {
548
                    $message = $this->__('Done! Item created.');
549
                } else {
550
                    $message = $this->__('Error! Creation attempt failed.');
551
                }
552
                break;
553 View Code Duplication
            case 'update':
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...
554
                if (true === $success) {
555
                    $message = $this->__('Done! Item updated.');
556
                } else {
557
                    $message = $this->__('Error! Update attempt failed.');
558
                }
559
                break;
560 View Code Duplication
            case 'delete':
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...
561
                if (true === $success) {
562
                    $message = $this->__('Done! Item deleted.');
563
                } else {
564
                    $message = $this->__('Error! Deletion attempt failed.');
565
                }
566
                break;
567
        }
568
    
569
        return $message;
570
    }
571
    
572
    /**
573
     * Add success or error message to session.
574
     *
575
     * @param array   $args    arguments from handleCommand method
576
     * @param Boolean $success true if this is a success, false for default error
577
     *
578
     * @throws RuntimeException Thrown if executing the workflow action fails
579
     */
580
    protected function addDefaultMessage($args, $success = false)
581
    {
582
        $message = $this->getDefaultMessage($args, $success);
583
        if (empty($message)) {
584
            return;
585
        }
586
    
587
        $flashType = true === $success ? 'status' : 'error';
588
        $this->request->getSession()->getFlashBag()->add($flashType, $message);
589
        $logArgs = ['app' => 'ZikulaRoutesModule', 'user' => $this->currentUserApi->get('uname'), 'entity' => $this->objectType, 'id' => $this->entityRef->createCompositeIdentifier()];
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\Core\Doctrine\EntityAccess as the method createCompositeIdentifier() does only exist in the following sub-classes of Zikula\Core\Doctrine\EntityAccess: Zikula\RoutesModule\Enti...ase\AbstractRouteEntity, Zikula\RoutesModule\Entity\RouteEntity. 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...
590
        if (true === $success) {
591
            $this->logger->notice('{app}: User {user} updated the {entity} with id {id}.', $logArgs);
592
        } else {
593
            $this->logger->error('{app}: User {user} tried to update the {entity} with id {id}, but failed.', $logArgs);
594
        }
595
    }
596
597
    /**
598
     * Input data processing called by handleCommand method.
599
     *
600
     * @param array $args Additional arguments
601
     */
602
    public function fetchInputData($args)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
603
    {
604
        // fetch posted data input values as an associative array
605
        $formData = $this->form->getData();
606
    
607
        if ($this->templateParameters['mode'] == 'create' && isset($this->form['repeatCreation']) && $this->form['repeatCreation']->getData() == 1) {
608
            $this->repeatCreateAction = true;
609
        }
610
    
611
        if (method_exists($this->entityRef, 'getCreatedBy')) {
612 View Code Duplication
            if (isset($this->form['moderationSpecificCreator']) && null !== $this->form['moderationSpecificCreator']->getData()) {
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...
613
                $this->entityRef->setCreatedBy($this->form['moderationSpecificCreator']->getData());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\Core\Doctrine\EntityAccess as the method setCreatedBy() does only exist in the following sub-classes of Zikula\Core\Doctrine\EntityAccess: Zikula\RoutesModule\Enti...ase\AbstractRouteEntity, Zikula\RoutesModule\Entity\RouteEntity. 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...
614
            }
615 View Code Duplication
            if (isset($this->form['moderationSpecificCreationDate']) && $this->form['moderationSpecificCreationDate']->getData() != '') {
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...
616
                $this->entityRef->setCreatedDate($this->form['moderationSpecificCreationDate']->getData());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Zikula\Core\Doctrine\EntityAccess as the method setCreatedDate() does only exist in the following sub-classes of Zikula\Core\Doctrine\EntityAccess: Zikula\RoutesModule\Enti...ase\AbstractRouteEntity, Zikula\RoutesModule\Entity\RouteEntity. 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...
617
            }
618
        }
619
    
620
        if (isset($this->form['additionalNotificationRemarks']) && $this->form['additionalNotificationRemarks']->getData() != '') {
621
            $this->request->getSession()->set('ZikulaRoutesModuleAdditionalNotificationRemarks', $this->form['additionalNotificationRemarks']->getData());
622
        }
623
    
624
        // return remaining form data
625
        return $formData;
626
    }
627
628
    /**
629
     * This method executes a certain workflow action.
630
     *
631
     * @param array $args Arguments from handleCommand method
632
     *
633
     * @return bool Whether everything worked well or not
634
     */
635
    public function applyAction(array $args = [])
636
    {
637
        // stub for subclasses
638
        return false;
639
    }
640
641
    /**
642
     * Sets optional locking api reference.
643
     *
644
     * @param LockingApiInterface $lockingApi
645
     */
646
    public function setLockingApi(LockingApiInterface $lockingApi)
647
    {
648
        $this->lockingApi = $lockingApi;
649
    }
650
}
651