Passed
Push — master ( 3109ad...89528c )
by Peter
02:44
created

ContentList   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 502
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 211
c 1
b 0
f 0
dl 0
loc 502
rs 8.4
wmc 50

18 Methods

Rating   Name   Duplication   Size   Complexity  
A addClasses() 0 13 2
A addWithLinks() 0 23 3
A addName() 0 13 3
A isUserAdvanced() 0 14 2
A addProtected() 0 23 3
A addWithImages() 0 23 3
A showAdvancedFields() 0 7 4
A addNewButtons() 0 13 1
A addIdentifier() 0 20 3
A addWithClasses() 0 23 3
A addButtons() 0 7 2
A __construct() 0 12 1
A addNewItems() 0 34 5
A addWithLabelLinks() 0 23 3
A addWithHtml() 0 23 3
A addExistingItems() 0 32 4
A addAddBtn() 0 17 4
A create() 0 27 1

How to fix   Complexity   

Complex Class

Complex classes like ContentList 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.

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

1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Website\Form\Factory;
6
7
use AbterPhp\Admin\Form\Factory\Base;
8
use AbterPhp\Framework\Constant\Html5;
9
use AbterPhp\Framework\Constant\Session;
10
use AbterPhp\Framework\Form\Container\CheckboxGroup;
11
use AbterPhp\Framework\Form\Container\FormGroup;
12
use AbterPhp\Framework\Form\Element\Input;
13
use AbterPhp\Framework\Form\Extra\DefaultButtons;
14
use AbterPhp\Framework\Form\Extra\Help;
15
use AbterPhp\Framework\Form\IForm;
16
use AbterPhp\Framework\Form\Label\Label;
17
use AbterPhp\Framework\Html\Component;
18
use AbterPhp\Framework\Html\Component\Button;
19
use AbterPhp\Framework\Html\Tag;
20
use AbterPhp\Framework\I18n\ITranslator;
21
use AbterPhp\Website\Constant\Authorization;
22
use AbterPhp\Website\Domain\Entities\ContentList as Entity;
23
use AbterPhp\Website\Domain\Entities\ContentListItem;
24
use AbterPhp\Website\Form\Factory\ContentList\Item as ItemFactory;
25
use AbterPhp\Website\Orm\ContentListItemRepo as ItemRepo;
26
use Casbin\Enforcer;
27
use Opulence\Orm\IEntity;
28
use Opulence\Sessions\ISession;
29
30
/**
31
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
32
 */
33
class ContentList extends Base
34
{
35
    private const NEW_ITEM_NAME = 'website:contentListItemNew';
36
37
    private const ITEM_TEMPLATE_CLASS         = 'item-template';
38
    private const NEW_ITEMS_CONTAINER_ID      = 'new-items';
39
    private const EXISTING_ITEMS_CONTAINER_ID = 'existing-items';
40
41
    /** @var ItemRepo */
42
    protected $itemRepo;
43
44
    /** @var ItemFactory */
45
    protected $itemFactory;
46
47
    /** @var Enforcer */
48
    protected $enforcer;
49
50
    /** @var bool */
51
    private $isNew = false;
52
53
    /** @var bool|null */
54
    protected $advancedAllowed;
55
56
    /**
57
     * ContentList constructor.
58
     *
59
     * @param ISession    $session
60
     * @param ITranslator $translator
61
     * @param ItemRepo    $itemRepo
62
     * @param ItemFactory $itemFactory
63
     * @param Enforcer    $enforcer
64
     */
65
    public function __construct(
66
        ISession $session,
67
        ITranslator $translator,
68
        ItemRepo $itemRepo,
69
        ItemFactory $itemFactory,
70
        Enforcer $enforcer
71
    ) {
72
        parent::__construct($session, $translator);
73
74
        $this->itemRepo    = $itemRepo;
75
        $this->itemFactory = $itemFactory;
76
        $this->enforcer    = $enforcer;
77
    }
78
79
    /**
80
     * @return bool
81
     * @throws \Casbin\Exceptions\CasbinException
82
     */
83
    protected function isUserAdvanced(): bool
84
    {
85
        if ($this->advancedAllowed !== null) {
86
            return $this->advancedAllowed;
87
        }
88
89
        $username              = $this->session->get(Session::USERNAME);
90
        $this->advancedAllowed = $this->enforcer->enforce(
91
            $username,
92
            Authorization::RESOURCE_LISTS,
93
            Authorization::ROLE_ADVANCED_WRITE
94
        );
95
96
        return $this->advancedAllowed;
97
    }
98
99
    /**
100
     * @param IEntity|null $entity
101
     *
102
     * @return bool
103
     * @throws \Casbin\Exceptions\CasbinException
104
     */
105
    protected function showAdvancedFields(?IEntity $entity): bool
106
    {
107
        if (null === $entity || !$entity->getId()) {
108
            return true;
109
        }
110
111
        return !$entity->isProtected() || $this->isUserAdvanced();
0 ignored issues
show
Bug introduced by
The method isProtected() does not exist on Opulence\Orm\IEntity. It seems like you code against a sub-type of Opulence\Orm\IEntity such as AbterPhp\Website\Domain\Entities\ContentList. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

111
        return !$entity->/** @scrutinizer ignore-call */ isProtected() || $this->isUserAdvanced();
Loading history...
112
    }
113
114
    /**
115
     * @param string       $action
116
     * @param string       $method
117
     * @param string       $showUrl
118
     * @param IEntity|null $entity
119
     *
120
     * @return IForm
121
     * @throws \Casbin\Exceptions\CasbinException
122
     */
123
    public function create(string $action, string $method, string $showUrl, ?IEntity $entity = null): IForm
124
    {
125
        assert($entity instanceof Entity, new \InvalidArgumentException());
126
127
        $this->isNew = ($entity->getName() == '');
128
129
        $this->createForm($action, $method)
130
            ->addDefaultElements()
131
            ->addName($entity)
132
            ->addIdentifier($entity)
133
            ->addClasses($entity)
134
            ->addProtected($entity)
135
            ->addWithLinks($entity)
136
            ->addWithLabelLinks($entity)
137
            ->addWithHtml($entity)
138
            ->addWithImages($entity)
139
            ->addWithClasses($entity)
140
            ->addExistingItems($entity)
141
            ->addNewItems($entity)
142
            ->addAddBtn($entity)
143
            ->addButtons($entity, $showUrl);
144
145
        $form = $this->form;
146
147
        $this->form = null;
148
149
        return $form;
150
    }
151
152
    /**
153
     * @param Entity $entity
154
     *
155
     * @return $this
156
     */
157
    protected function addName(Entity $entity): self
158
    {
159
        // Identifier of protected lists can only be set by advanced users
160
        if ($entity->isProtected() && !$this->isUserAdvanced()) {
161
            return $this;
162
        }
163
164
        $input = new Input('name', 'name', $entity->getName());
165
        $label = new Label('name', 'website:contentListName');
166
167
        $this->form[] = new FormGroup($input, $label, null, [], [Html5::ATTR_CLASS => FormGroup::CLASS_REQUIRED]);
168
169
        return $this;
170
    }
171
172
    /**
173
     * @param Entity $entity
174
     *
175
     * @return $this
176
     */
177
    protected function addIdentifier(Entity $entity): self
178
    {
179
        // Identifier of protected lists can only be set by advanced users
180
        if ($entity->isProtected() && !$this->isUserAdvanced()) {
181
            return $this;
182
        }
183
184
        $input = new Input(
185
            'identifier',
186
            'identifier',
187
            $entity->getIdentifier(),
188
            [],
189
            [Html5::ATTR_CLASS => 'semi-auto']
190
        );
191
        $label = new Label('identifier', 'website:contentListIdentifier');
192
        $help  = new Help('website:contentListIdentifierHelp');
193
194
        $this->form[] = new FormGroup($input, $label, $help);
195
196
        return $this;
197
    }
198
199
    /**
200
     * @param Entity $entity
201
     *
202
     * @return $this
203
     */
204
    protected function addClasses(Entity $entity): self
205
    {
206
        if (!$this->showAdvancedFields($entity)) {
207
            return $this;
208
        }
209
210
        $input = new Input('classes', 'classes', $entity->getClasses());
211
        $label = new Label('classes', 'website:contentListClasses');
212
        $help  = new Help('website:contentListClassesHelp');
213
214
        $this->form[] = new FormGroup($input, $label, $help);
215
216
        return $this;
217
    }
218
219
    /**
220
     * @param Entity $entity
221
     *
222
     * @return $this
223
     */
224
    protected function addProtected(Entity $entity): self
225
    {
226
        if (!$this->showAdvancedFields($entity)) {
227
            return $this;
228
        }
229
230
        $attributes = [Html5::ATTR_TYPE => Input::TYPE_CHECKBOX];
231
        if ($entity->isProtected()) {
232
            $attributes[Html5::ATTR_CHECKED] = null;
233
        }
234
        $input = new Input(
235
            'protected',
236
            'protected',
237
            '1',
238
            [],
239
            $attributes
240
        );
241
        $label = new Label('protected', 'website:contentListProtected');
242
        $help  = new Help('website:contentListProtectedHelp');
243
244
        $this->form[] = new CheckboxGroup($input, $label, $help, [], [Html5::ATTR_ID => 'protected-container']);
245
246
        return $this;
247
    }
248
249
    /**
250
     * @param Entity $entity
251
     *
252
     * @return INode
0 ignored issues
show
Bug introduced by
The type AbterPhp\Website\Form\Factory\INode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
253
     */
254
    protected function addWithLinks(Entity $entity): self
255
    {
256
        if (!$this->showAdvancedFields($entity)) {
257
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AbterPhp\Website\Form\Factory\ContentList which is incompatible with the documented return type AbterPhp\Website\Form\Factory\INode.
Loading history...
258
        }
259
260
        $attributes = [Html5::ATTR_TYPE => Input::TYPE_CHECKBOX];
261
        if ($entity->isWithLinks()) {
262
            $attributes[Html5::ATTR_CHECKED] = null;
263
        }
264
        $input = new Input(
265
            'with_links',
266
            'with_links',
267
            '1',
268
            [],
269
            $attributes
270
        );
271
        $label = new Label('with_links', 'website:contentListWithLinks');
272
        $help  = new Help('website:contentListWithLinksHelp');
273
274
        $this->form[] = new CheckboxGroup($input, $label, $help, [], [Html5::ATTR_ID => 'withLinks-container']);
275
276
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AbterPhp\Website\Form\Factory\ContentList which is incompatible with the documented return type AbterPhp\Website\Form\Factory\INode.
Loading history...
277
    }
278
279
    /**
280
     * @param Entity $entity
281
     *
282
     * @return INode
283
     */
284
    protected function addWithLabelLinks(Entity $entity): self
285
    {
286
        if (!$this->showAdvancedFields($entity)) {
287
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AbterPhp\Website\Form\Factory\ContentList which is incompatible with the documented return type AbterPhp\Website\Form\Factory\INode.
Loading history...
288
        }
289
290
        $attributes = [Html5::ATTR_TYPE => Input::TYPE_CHECKBOX];
291
        if ($entity->isWithLabelLinks()) {
292
            $attributes[Html5::ATTR_CHECKED] = null;
293
        }
294
        $input = new Input(
295
            'with_label_links',
296
            'with_label_links',
297
            '1',
298
            [],
299
            $attributes
300
        );
301
        $label = new Label('with_label_links', 'website:contentListWithLabelLinks');
302
        $help  = new Help('website:contentListWithLabelLinksHelp');
303
304
        $this->form[] = new CheckboxGroup($input, $label, $help, [], [Html5::ATTR_ID => 'withLabelLinks-container']);
305
306
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AbterPhp\Website\Form\Factory\ContentList which is incompatible with the documented return type AbterPhp\Website\Form\Factory\INode.
Loading history...
307
    }
308
309
    /**
310
     * @param Entity $entity
311
     *
312
     * @return INode
313
     */
314
    protected function addWithHtml(Entity $entity): self
315
    {
316
        if (!$this->showAdvancedFields($entity)) {
317
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AbterPhp\Website\Form\Factory\ContentList which is incompatible with the documented return type AbterPhp\Website\Form\Factory\INode.
Loading history...
318
        }
319
320
        $attributes = [Html5::ATTR_TYPE => Input::TYPE_CHECKBOX];
321
        if ($entity->isWithHtml()) {
322
            $attributes[Html5::ATTR_CHECKED] = null;
323
        }
324
        $input = new Input(
325
            'with_html',
326
            'with_html',
327
            '1',
328
            [],
329
            $attributes
330
        );
331
        $label = new Label('with_html', 'website:contentListWithHtml');
332
        $help  = new Help('website:contentListWithHtmlHelp');
333
334
        $this->form[] = new CheckboxGroup($input, $label, $help, [], [Html5::ATTR_ID => 'withHtml-container']);
335
336
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type AbterPhp\Website\Form\Factory\ContentList which is incompatible with the documented return type AbterPhp\Website\Form\Factory\INode.
Loading history...
337
    }
338
339
    /**
340
     * @param Entity $entity
341
     *
342
     * @return $this
343
     */
344
    protected function addWithImages(Entity $entity): self
345
    {
346
        if (!$this->showAdvancedFields($entity)) {
347
            return $this;
348
        }
349
350
        $attributes = [Html5::ATTR_TYPE => Input::TYPE_CHECKBOX];
351
        if ($entity->isWithImages()) {
352
            $attributes[Html5::ATTR_CHECKED] = null;
353
        }
354
        $input = new Input(
355
            'with_images',
356
            'with_images',
357
            '1',
358
            [],
359
            $attributes
360
        );
361
        $label = new Label('with_images', 'website:contentListWithImages');
362
        $help  = new Help('website:contentListWithImagesHelp');
363
364
        $this->form[] = new CheckboxGroup($input, $label, $help, [], [Html5::ATTR_ID => 'withImages-container']);
365
366
        return $this;
367
    }
368
369
    /**
370
     * @param Entity $entity
371
     *
372
     * @return $this
373
     */
374
    protected function addWithClasses(Entity $entity): self
375
    {
376
        if (!$this->showAdvancedFields($entity)) {
377
            return $this;
378
        }
379
380
        $attributes = [Html5::ATTR_TYPE => Input::TYPE_CHECKBOX];
381
        if ($entity->isWithClasses()) {
382
            $attributes[Html5::ATTR_CHECKED] = null;
383
        }
384
        $input = new Input(
385
            'with_classes',
386
            'with_classes',
387
            '1',
388
            [],
389
            $attributes
390
        );
391
        $label = new Label('with_classes', 'website:contentListWithClasses');
392
        $help  = new Help('website:contentListWithClassesHelp');
393
394
        $this->form[] = new CheckboxGroup($input, $label, $help, [], [Html5::ATTR_ID => 'withClasses-container']);
395
396
        return $this;
397
    }
398
399
    /**
400
     * @param Entity $entity
401
     *
402
     * @return $this
403
     */
404
    protected function addExistingItems(Entity $entity): self
405
    {
406
        // There's no reason to check existing items during creation
407
        if (!$entity->getId()) {
408
            return $this;
409
        }
410
411
        $links      = $entity->isWithLinks();
412
        $labelLinks = $entity->isWithLabelLinks();
413
        $html       = $entity->isWithHtml();
414
        $images     = $entity->isWithImages();
415
        $classes    = $entity->isWithClasses();
416
417
        $containerAttribs = [Html5::ATTR_ID => static::EXISTING_ITEMS_CONTAINER_ID];
418
        $container        = new Component(null, [], $containerAttribs, Html5::TAG_SECTION);
419
420
        /** @var ContentListItem[] $items */
421
        $items = $this->itemRepo->getByListId($entity->getId());
422
        foreach ($items as $item) {
423
            $fieldset   = new Component(null, [], [], Html5::TAG_FIELDSET);
424
            $fieldset[] = new Tag($item->getLabel(), [], [], Html5::TAG_LEGEND);
425
426
            $components = $this->itemFactory->create($item, $links, $labelLinks, $html, $images, $classes);
427
            foreach ($components as $component) {
428
                $fieldset[] = $component;
429
            }
430
            $container[] = $fieldset;
431
        }
432
433
        $this->form[] = $container;
434
435
        return $this;
436
    }
437
438
    /**
439
     * @param Entity $entity
440
     *
441
     * @return $this
442
     */
443
    protected function addNewItems(Entity $entity): self
444
    {
445
        // New items can not be added during creation
446
        if (!$entity->getId()) {
447
            return $this;
448
        }
449
        // New items can only be added to protected lists by advanced users
450
        if ($entity->isProtected() && !$this->isUserAdvanced()) {
451
            return $this;
452
        }
453
454
        $links      = $entity->isWithLinks();
455
        $labelLinks = $entity->isWithLabelLinks();
456
        $html       = $entity->isWithHtml();
457
        $images     = $entity->isWithImages();
458
        $classes    = $entity->isWithClasses();
459
460
        $containerAttribs = [Html5::ATTR_ID => static::NEW_ITEMS_CONTAINER_ID];
461
        $container        = new Component(null, [], $containerAttribs, Html5::TAG_SECTION);
462
463
        $itemAttribs = [Html5::ATTR_CLASS => static::ITEM_TEMPLATE_CLASS];
464
        $item        = new Component(null, [], $itemAttribs, Html5::TAG_FIELDSET);
465
        $item[]      = new Tag(static::NEW_ITEM_NAME, [], [], Html5::TAG_LEGEND);
466
467
        $components = $this->itemFactory->create(null, $links, $labelLinks, $html, $images, $classes);
468
        foreach ($components as $component) {
469
            $item[] = $component;
470
        }
471
472
        $container[] = $item;
473
474
        $this->form[] = $container;
475
476
        return $this;
477
    }
478
479
    /**
480
     * @param Entity $entity
481
     *
482
     * @return $this
483
     */
484
    protected function addAddBtn(Entity $entity): ContentList
485
    {
486
        // New items can not be added during creation
487
        if (!$entity->getId()) {
488
            return $this;
489
        }
490
        // New items can only be added to protected lists by advanced users
491
        if ($entity->isProtected() && !$this->isUserAdvanced()) {
492
            return $this;
493
        }
494
495
        $i   = new Component('add', [Component::INTENT_SMALL, Component::INTENT_ICON], [], Html5::TAG_I);
496
        $btn = new Button($i, [Button::INTENT_FAB, Button::INTENT_PRIMARY], [Html5::ATTR_TYPE => Button::TYPE_BUTTON]);
497
498
        $this->form[] = new Component($btn, [], [Html5::ATTR_ID => 'add-item-container'], Html5::TAG_DIV);
499
500
        return $this;
501
    }
502
503
    /**
504
     * @param string $showUrl
505
     *
506
     * @return Base
507
     */
508
    protected function addButtons(Entity $entity, string $showUrl): Base
509
    {
510
        if ($entity->getName()) {
511
            return parent::addDefaultButtons($showUrl);
512
        }
513
514
        return $this->addNewButtons($showUrl);
515
    }
516
517
    /**
518
     * @param string $showUrl
519
     *
520
     * @return Base
521
     */
522
    protected function addNewButtons(string $showUrl): Base
523
    {
524
        $buttons = new DefaultButtons();
525
526
        $buttons
527
            ->addSaveAndEdit(Button::INTENT_PRIMARY, Button::INTENT_FORM)
528
            ->addBackToGrid($showUrl)
529
            ->addSaveAndBack(Button::INTENT_WARNING, Button::INTENT_FORM)
530
            ->addSaveAndCreate();
531
532
        $this->form[] = $buttons;
533
534
        return $this;
535
    }
536
}
537