| Total Complexity | 50 |
| Total Lines | 502 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
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 |
||
| 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 Entity|null $entity |
||
| 101 | * |
||
| 102 | * @return bool |
||
| 103 | * @throws \Casbin\Exceptions\CasbinException |
||
| 104 | */ |
||
| 105 | protected function showAdvancedFields(?Entity $entity): bool |
||
| 106 | { |
||
| 107 | if (null === $entity || !$entity->getId()) { |
||
| 108 | return true; |
||
| 109 | } |
||
| 110 | |||
| 111 | return !$entity->isProtected() || $this->isUserAdvanced(); |
||
| 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 |
||
| 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 $this |
||
| 253 | */ |
||
| 254 | protected function addWithLinks(Entity $entity): self |
||
| 277 | } |
||
| 278 | |||
| 279 | /** |
||
| 280 | * @param Entity $entity |
||
| 281 | * |
||
| 282 | * @return $this |
||
| 283 | */ |
||
| 284 | protected function addWithLabelLinks(Entity $entity): self |
||
| 285 | { |
||
| 286 | if (!$this->showAdvancedFields($entity)) { |
||
| 287 | return $this; |
||
| 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; |
||
| 307 | } |
||
| 308 | |||
| 309 | /** |
||
| 310 | * @param Entity $entity |
||
| 311 | * |
||
| 312 | * @return $this |
||
| 313 | */ |
||
| 314 | protected function addWithHtml(Entity $entity): self |
||
| 315 | { |
||
| 316 | if (!$this->showAdvancedFields($entity)) { |
||
| 317 | return $this; |
||
| 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; |
||
| 337 | } |
||
| 338 | |||
| 339 | /** |
||
| 340 | * @param Entity $entity |
||
| 341 | * |
||
| 342 | * @return $this |
||
| 343 | */ |
||
| 344 | protected function addWithImages(Entity $entity): self |
||
| 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): self |
||
| 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 |
||
| 535 | } |
||
| 536 | } |
||
| 537 |