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 classes like BaseController 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 BaseController, and based on these observations, apply Extract Interface, too.
| 1 | <?php namespace Limoncello\Flute\Http; |
||
| 46 | abstract class BaseController implements ControllerInterface |
||
| 47 | { |
||
| 48 | use CreateResponsesTrait; |
||
| 49 | |||
| 50 | /** API class name */ |
||
| 51 | const API_CLASS = null; |
||
| 52 | |||
| 53 | /** JSON API Schema class name */ |
||
| 54 | const SCHEMA_CLASS = null; |
||
| 55 | |||
| 56 | /** JSON API validation rules set class */ |
||
| 57 | const ON_CREATE_VALIDATION_RULES_SET_CLASS = null; |
||
| 58 | |||
| 59 | /** JSON API validation rules set class */ |
||
| 60 | const ON_UPDATE_VALIDATION_RULES_SET_CLASS = null; |
||
| 61 | |||
| 62 | /** |
||
| 63 | * @inheritdoc |
||
| 64 | */ |
||
| 65 | 12 | View Code Duplication | public static function index( |
|
|
|||
| 66 | array $routeParams, |
||
| 67 | ContainerInterface $container, |
||
| 68 | ServerRequestInterface $request |
||
| 69 | ): ResponseInterface { |
||
| 70 | // By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
||
| 71 | 12 | $parser = static::configureOnIndexParser(static::createQueryParser($container)) |
|
| 72 | 12 | ->parse($request->getQueryParams()); |
|
| 73 | 11 | $mapper = static::createParameterMapper($container); |
|
| 74 | 11 | $api = static::createApi($container); |
|
| 75 | |||
| 76 | 11 | $models = $mapper->applyQueryParameters($parser, $api)->index(); |
|
| 77 | |||
| 78 | 11 | $responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
| 79 | 11 | $response = ($models->getData()) === null ? |
|
| 80 | 11 | $responses->getCodeResponse(404) : $responses->getContentResponse($models); |
|
| 81 | |||
| 82 | 11 | return $response; |
|
| 83 | } |
||
| 84 | |||
| 85 | /** |
||
| 86 | * @inheritdoc |
||
| 87 | */ |
||
| 88 | 1 | public static function create( |
|
| 89 | array $routeParams, |
||
| 90 | ContainerInterface $container, |
||
| 91 | ServerRequestInterface $request |
||
| 92 | ): ResponseInterface { |
||
| 93 | 1 | list ($index, $api) = static::createImpl($container, $request); |
|
| 94 | |||
| 95 | 1 | $data = $api->read($index)->getData(); |
|
| 96 | |||
| 97 | 1 | $response = static::createResponses($container, $request)->getCreatedResponse($data); |
|
| 98 | |||
| 99 | 1 | return $response; |
|
| 100 | } |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @inheritdoc |
||
| 104 | */ |
||
| 105 | 1 | View Code Duplication | public static function read( |
| 106 | array $routeParams, |
||
| 107 | ContainerInterface $container, |
||
| 108 | ServerRequestInterface $request |
||
| 109 | ): ResponseInterface { |
||
| 110 | // By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
||
| 111 | 1 | $parser = static::configureOnReadParser(static::createQueryParser($container)) |
|
| 112 | 1 | ->parse($request->getQueryParams()); |
|
| 113 | 1 | $mapper = static::createParameterMapper($container); |
|
| 114 | |||
| 115 | 1 | $index = $routeParams[static::ROUTE_KEY_INDEX]; |
|
| 116 | 1 | $modelData = $mapper->applyQueryParameters($parser, static::createApi($container))->read($index)->getData(); |
|
| 117 | |||
| 118 | 1 | $responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
| 119 | 1 | $response = $modelData === null ? |
|
| 120 | 1 | $responses->getCodeResponse(404) : $responses->getContentResponse($modelData); |
|
| 121 | |||
| 122 | 1 | return $response; |
|
| 123 | } |
||
| 124 | |||
| 125 | /** |
||
| 126 | * @inheritdoc |
||
| 127 | */ |
||
| 128 | 4 | View Code Duplication | public static function update( |
| 129 | array $routeParams, |
||
| 130 | ContainerInterface $container, |
||
| 131 | ServerRequestInterface $request |
||
| 132 | ): ResponseInterface { |
||
| 133 | 4 | list ($updated, $index, $api) = static::updateImpl($routeParams, $container, $request); |
|
| 134 | |||
| 135 | 1 | $responses = static::createResponses($container, $request); |
|
| 136 | 1 | if ($updated > 0) { |
|
| 137 | 1 | $modelData = $api->read($index)->getData(); |
|
| 138 | 1 | $response = $responses->getContentResponse($modelData); |
|
| 139 | } else { |
||
| 140 | $response = $responses->getCodeResponse(404); |
||
| 141 | } |
||
| 142 | |||
| 143 | 1 | return $response; |
|
| 144 | } |
||
| 145 | |||
| 146 | /** |
||
| 147 | * @inheritdoc |
||
| 148 | */ |
||
| 149 | 1 | public static function delete( |
|
| 150 | array $routeParams, |
||
| 151 | ContainerInterface $container, |
||
| 152 | ServerRequestInterface $request |
||
| 153 | ): ResponseInterface { |
||
| 154 | 1 | static::createApi($container)->remove($routeParams[static::ROUTE_KEY_INDEX]); |
|
| 155 | |||
| 156 | 1 | $response = static::createResponses($container, $request)->getCodeResponse(204); |
|
| 157 | |||
| 158 | 1 | return $response; |
|
| 159 | } |
||
| 160 | |||
| 161 | /** |
||
| 162 | * @param string $index |
||
| 163 | * @param string $relationshipName |
||
| 164 | * @param ContainerInterface $container |
||
| 165 | * @param ServerRequestInterface $request |
||
| 166 | * |
||
| 167 | * @return ResponseInterface |
||
| 168 | */ |
||
| 169 | 2 | View Code Duplication | protected static function readRelationship( |
| 170 | string $index, |
||
| 171 | string $relationshipName, |
||
| 172 | ContainerInterface $container, |
||
| 173 | ServerRequestInterface $request |
||
| 174 | ): ResponseInterface { |
||
| 175 | // By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
||
| 176 | 2 | $parser = static::configureOnReadRelationshipParser( |
|
| 177 | 2 | $relationshipName, |
|
| 178 | 2 | static::createQueryParser($container))->parse($request->getQueryParams() |
|
| 179 | ); |
||
| 180 | |||
| 181 | 2 | $relData = static::readRelationshipData($index, $relationshipName, $container, $parser); |
|
| 182 | 2 | $responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
| 183 | 2 | $response = $relData->getData() === null ? |
|
| 184 | 2 | $responses->getCodeResponse(404) : $responses->getContentResponse($relData); |
|
| 185 | |||
| 186 | 2 | return $response; |
|
| 187 | } |
||
| 188 | |||
| 189 | /** |
||
| 190 | * @param string $index |
||
| 191 | * @param string $relationshipName |
||
| 192 | * @param ContainerInterface $container |
||
| 193 | * @param ServerRequestInterface $request |
||
| 194 | * |
||
| 195 | * @return ResponseInterface |
||
| 196 | */ |
||
| 197 | 1 | View Code Duplication | protected static function readRelationshipIdentifiers( |
| 198 | string $index, |
||
| 199 | string $relationshipName, |
||
| 200 | ContainerInterface $container, |
||
| 201 | ServerRequestInterface $request |
||
| 202 | ): ResponseInterface { |
||
| 203 | // By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
||
| 204 | 1 | $parser = static::configureOnReadRelationshipIdentifiersParser( |
|
| 205 | 1 | $relationshipName, |
|
| 206 | 1 | static::createQueryParser($container) |
|
| 207 | 1 | )->parse($request->getQueryParams()); |
|
| 208 | |||
| 209 | 1 | $relData = static::readRelationshipData($index, $relationshipName, $container, $parser); |
|
| 210 | 1 | $responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
| 211 | 1 | $response = $relData->getData() === null ? |
|
| 212 | 1 | $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData); |
|
| 213 | |||
| 214 | 1 | return $response; |
|
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * @param ContainerInterface $container |
||
| 219 | * @param string|null $class |
||
| 220 | * |
||
| 221 | * @return CrudInterface |
||
| 222 | */ |
||
| 223 | 21 | protected static function createApi(ContainerInterface $container, string $class = null): CrudInterface |
|
| 231 | |||
| 232 | /** |
||
| 233 | * @param ContainerInterface $container |
||
| 234 | * @param ServerRequestInterface $request |
||
| 235 | * |
||
| 236 | * @return array |
||
| 237 | */ |
||
| 238 | 7 | protected static function readJsonFromRequest(ContainerInterface $container, ServerRequestInterface $request): array |
|
| 254 | |||
| 255 | /** |
||
| 256 | * @param array $routeParams |
||
| 257 | * @param ContainerInterface $container |
||
| 258 | * @param array $jsonData |
||
| 259 | * |
||
| 260 | * @return array |
||
| 261 | * |
||
| 262 | * @SuppressWarnings(PHPMD.ElseExpression) |
||
| 263 | */ |
||
| 264 | 3 | protected static function normalizeIndexValueOnUpdate( |
|
| 294 | |||
| 295 | /** |
||
| 296 | * @param int|string $parentIndex |
||
| 297 | * @param string $relationshipName |
||
| 298 | * @param int|string $childIndex |
||
| 299 | * @param string $childApiClass |
||
| 300 | * @param ContainerInterface $container |
||
| 301 | * @param ServerRequestInterface $request |
||
| 302 | * |
||
| 303 | * @return ResponseInterface |
||
| 304 | */ |
||
| 305 | 1 | protected static function deleteInRelationship( |
|
| 328 | |||
| 329 | /** @noinspection PhpTooManyParametersInspection |
||
| 330 | * @param int|string $parentIndex |
||
| 331 | * @param string $relationshipName |
||
| 332 | * @param int|string $childIndex |
||
| 333 | * @param array $attributes |
||
| 334 | * @param array $toMany |
||
| 335 | * @param string $childApiClass |
||
| 336 | * @param ContainerInterface $container |
||
| 337 | * @param ServerRequestInterface $request |
||
| 338 | * |
||
| 339 | * @return ResponseInterface |
||
| 340 | */ |
||
| 341 | 2 | View Code Duplication | protected static function updateInRelationship( |
| 371 | |||
| 372 | /** |
||
| 373 | * @param ContainerInterface $container |
||
| 374 | * |
||
| 375 | * @return JsonApiValidatorInterface |
||
| 376 | */ |
||
| 377 | 1 | View Code Duplication | protected static function createOnCreateValidator(ContainerInterface $container): JsonApiValidatorInterface |
| 386 | |||
| 387 | /** |
||
| 388 | * @param ContainerInterface $container |
||
| 389 | * |
||
| 390 | * @return JsonApiValidatorInterface |
||
| 391 | */ |
||
| 392 | 2 | View Code Duplication | protected static function createOnUpdateValidator(ContainerInterface $container): JsonApiValidatorInterface |
| 401 | |||
| 402 | /** |
||
| 403 | * @param ContainerInterface $container |
||
| 404 | * @param string $rulesSetClass |
||
| 405 | * |
||
| 406 | * @return JsonApiValidatorInterface |
||
| 407 | */ |
||
| 408 | 5 | protected static function createJsonApiValidator( |
|
| 418 | |||
| 419 | /** |
||
| 420 | * @param ContainerInterface $container |
||
| 421 | * |
||
| 422 | * @return QueryParserInterface |
||
| 423 | */ |
||
| 424 | 16 | protected static function createQueryParser(ContainerInterface $container): QueryParserInterface |
|
| 428 | |||
| 429 | /** |
||
| 430 | * @param QueryParserInterface $parser |
||
| 431 | * |
||
| 432 | * @return QueryParserInterface |
||
| 433 | */ |
||
| 434 | 12 | protected static function configureOnIndexParser(QueryParserInterface $parser): QueryParserInterface |
|
| 438 | |||
| 439 | /** |
||
| 440 | * @param QueryParserInterface $parser |
||
| 441 | * |
||
| 442 | * @return QueryParserInterface |
||
| 443 | */ |
||
| 444 | 1 | protected static function configureOnReadParser(QueryParserInterface $parser): QueryParserInterface |
|
| 448 | |||
| 449 | /** |
||
| 450 | * @param string $name |
||
| 451 | * @param QueryParserInterface $parser |
||
| 452 | * |
||
| 453 | * @return QueryParserInterface |
||
| 454 | * |
||
| 455 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
||
| 456 | */ |
||
| 457 | 2 | protected static function configureOnReadRelationshipParser(/** @noinspection PhpUnusedParameterInspection */ |
|
| 463 | |||
| 464 | /** |
||
| 465 | * @param string $name |
||
| 466 | * @param QueryParserInterface $parser |
||
| 467 | * |
||
| 468 | * @return QueryParserInterface |
||
| 469 | * |
||
| 470 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
||
| 471 | */ |
||
| 472 | 1 | protected static function configureOnReadRelationshipIdentifiersParser( |
|
| 479 | |||
| 480 | /** |
||
| 481 | * @param ContainerInterface $container |
||
| 482 | * |
||
| 483 | * @return ParametersMapperInterface |
||
| 484 | */ |
||
| 485 | 15 | protected static function createParameterMapper(ContainerInterface $container): ParametersMapperInterface |
|
| 496 | |||
| 497 | /** |
||
| 498 | * @param ContainerInterface $container |
||
| 499 | * @param array $captures |
||
| 500 | * @param string $schemeClass |
||
| 501 | * |
||
| 502 | * @return array |
||
| 503 | */ |
||
| 504 | 4 | protected static function mapSchemeDataToModelData( |
|
| 544 | |||
| 545 | /** |
||
| 546 | * @param string $index |
||
| 547 | * @param string $relationshipName |
||
| 548 | * @param ContainerInterface $container |
||
| 549 | * @param QueryParserInterface $parser |
||
| 550 | * |
||
| 551 | * @return PaginatedDataInterface |
||
| 552 | */ |
||
| 553 | 3 | private static function readRelationshipData( |
|
| 568 | |||
| 569 | /** |
||
| 570 | * @param ContainerInterface $container |
||
| 571 | * @param ServerRequestInterface $request |
||
| 572 | * |
||
| 573 | * @return array |
||
| 574 | */ |
||
| 575 | 1 | protected static function createImpl( |
|
| 591 | |||
| 592 | /** |
||
| 593 | * @param array $routeParams |
||
| 594 | * @param ContainerInterface $container |
||
| 595 | * @param ServerRequestInterface $request |
||
| 596 | * |
||
| 597 | * @return array [int $updated, string $index, CrudInterface $api] |
||
| 598 | */ |
||
| 599 | 4 | protected static function updateImpl( |
|
| 620 | |||
| 621 | /** |
||
| 622 | * @param $parentIndex |
||
| 623 | * @param string $relationshipName |
||
| 624 | * @param $childIndex |
||
| 625 | * @param array $attributes |
||
| 626 | * @param array $toMany |
||
| 627 | * @param string $childApiClass |
||
| 628 | * @param ContainerInterface $container |
||
| 629 | * |
||
| 630 | * @return array |
||
| 631 | */ |
||
| 632 | 2 | private static function updateInRelationshipImpl( |
|
| 655 | |||
| 656 | /** |
||
| 657 | * @param ContainerInterface $container |
||
| 658 | * @param string $namespace |
||
| 659 | * |
||
| 660 | * @return FormatterInterface |
||
| 661 | */ |
||
| 662 | 2 | protected static function createMessageFormatter( |
|
| 672 | } |
||
| 673 |
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.