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 ContentRepository 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 ContentRepository, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 41 | class ContentRepository implements RepositoryInterface |
||
| 42 | { |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Tell whether it is a raw result (array) or object being returned. |
||
| 46 | * |
||
| 47 | * @var bool |
||
| 48 | */ |
||
| 49 | protected $rawResult = false; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * The data type to be returned, e.g fe_users, fe_groups, tt_content, etc... |
||
| 53 | * |
||
| 54 | * @var string |
||
| 55 | */ |
||
| 56 | protected $dataType; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * The source field is useful in the context of MM relations to know who is the caller |
||
| 60 | * e.g findByItems which eventually corresponds to a field name. |
||
| 61 | * |
||
| 62 | * @var string |
||
| 63 | */ |
||
| 64 | protected $sourceFieldName = ''; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * @var array |
||
| 68 | */ |
||
| 69 | protected $errorMessages = array(); |
||
| 70 | |||
| 71 | /** |
||
| 72 | * @var QuerySettingsInterface |
||
| 73 | */ |
||
| 74 | protected $defaultQuerySettings; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * Constructor |
||
| 78 | * |
||
| 79 | * @param string $dataType |
||
| 80 | */ |
||
| 81 | public function __construct($dataType) |
||
| 85 | |||
| 86 | /** |
||
| 87 | * Returns all objects of this repository. |
||
| 88 | * |
||
| 89 | * @return Content[] |
||
| 90 | */ |
||
| 91 | public function findAll() |
||
| 96 | |||
| 97 | /** |
||
| 98 | * Returns all "distinct" values for a given property. |
||
| 99 | * |
||
| 100 | * @param string $propertyName |
||
| 101 | * @param Matcher $matcher |
||
| 102 | * @return Content[] |
||
| 103 | */ |
||
| 104 | View Code Duplication | public function findDistinctValues($propertyName, Matcher $matcher = null) |
|
| 128 | |||
| 129 | /** |
||
| 130 | * Returns all "distinct" values for a given property. |
||
| 131 | * |
||
| 132 | * @param string $propertyName |
||
| 133 | * @param Matcher $matcher |
||
| 134 | * @return int |
||
| 135 | */ |
||
| 136 | View Code Duplication | public function countDistinctValues($propertyName, Matcher $matcher = null) |
|
| 160 | |||
| 161 | /** |
||
| 162 | * Finds an object matching the given identifier. |
||
| 163 | * |
||
| 164 | * @param int $uid The identifier of the object to find |
||
| 165 | * @return Content|null |
||
| 166 | * @api |
||
| 167 | */ |
||
| 168 | public function findByUid($uid) |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Finds all Contents given specified matches. |
||
| 175 | * |
||
| 176 | * @param string $propertyName |
||
| 177 | * @param array $values |
||
| 178 | * @return Content[] |
||
| 179 | */ |
||
| 180 | public function findIn($propertyName, array $values) |
||
| 186 | |||
| 187 | /** |
||
| 188 | * Finds all Contents given specified matches. |
||
| 189 | * |
||
| 190 | * @param Matcher $matcher |
||
| 191 | * @param Order $order The order |
||
| 192 | * @param int $limit |
||
| 193 | * @param int $offset |
||
| 194 | * @return Content[] |
||
| 195 | */ |
||
| 196 | public function findBy(Matcher $matcher, Order $order = null, $limit = null, $offset = null) |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Find one Content object given specified matches. |
||
| 234 | * |
||
| 235 | * @param Matcher $matcher |
||
| 236 | * @return Content |
||
| 237 | */ |
||
| 238 | public function findOneBy(Matcher $matcher) |
||
| 257 | |||
| 258 | /** |
||
| 259 | * Count all Contents given specified matches. |
||
| 260 | * |
||
| 261 | * @param Matcher $matcher |
||
| 262 | * @return int |
||
| 263 | */ |
||
| 264 | public function countBy(Matcher $matcher) |
||
| 277 | |||
| 278 | /** |
||
| 279 | * Update a content with new information. |
||
| 280 | * |
||
| 281 | * @param Content $content |
||
| 282 | * @param $language |
||
| 283 | * @return bool |
||
| 284 | */ |
||
| 285 | View Code Duplication | public function localize($content, $language) |
|
| 299 | |||
| 300 | /** |
||
| 301 | * Update a content with new information. |
||
| 302 | * |
||
| 303 | * @param Content $content |
||
| 304 | * @return bool |
||
| 305 | */ |
||
| 306 | public function update($content) |
||
| 319 | |||
| 320 | /** |
||
| 321 | * Removes an object from this repository. |
||
| 322 | * |
||
| 323 | * @param Content $content |
||
| 324 | * @return boolean |
||
| 325 | */ |
||
| 326 | public function remove($content) |
||
| 335 | |||
| 336 | /** |
||
| 337 | * Move a content within this repository. |
||
| 338 | * The $target corresponds to the pid to move the records to. |
||
| 339 | * It can also be a negative value in case of sorting. The negative value would be the uid of its predecessor. |
||
| 340 | * |
||
| 341 | * @param Content $content |
||
| 342 | * @param string $target |
||
| 343 | * @return bool |
||
| 344 | */ |
||
| 345 | View Code Duplication | public function move($content, $target) |
|
| 358 | |||
| 359 | /** |
||
| 360 | * Copy a content within this repository. |
||
| 361 | * |
||
| 362 | * @param Content $content |
||
| 363 | * @return bool |
||
| 364 | */ |
||
| 365 | View Code Duplication | public function copy($content, $target) |
|
| 378 | |||
| 379 | /** |
||
| 380 | * Adds an object to this repository. |
||
| 381 | * |
||
| 382 | * @param object $object The object to add |
||
| 383 | * @throws \BadMethodCallException |
||
| 384 | * @return void |
||
| 385 | * @api |
||
| 386 | */ |
||
| 387 | public function add($object) |
||
| 391 | |||
| 392 | /** |
||
| 393 | * Returns the total number objects of this repository. |
||
| 394 | * |
||
| 395 | * @return integer The object count |
||
| 396 | * @api |
||
| 397 | */ |
||
| 398 | public function countAll() |
||
| 403 | |||
| 404 | /** |
||
| 405 | * Removes all objects of this repository as if remove() was called for |
||
| 406 | * all of them. |
||
| 407 | * |
||
| 408 | * @return void |
||
| 409 | * @api |
||
| 410 | */ |
||
| 411 | public function removeAll() |
||
| 415 | |||
| 416 | /** |
||
| 417 | * Finds an object matching the given identifier. |
||
| 418 | * |
||
| 419 | * @param mixed $identifier The identifier of the object to find |
||
| 420 | * @return Content|null |
||
| 421 | * @api |
||
| 422 | */ |
||
| 423 | public function findByIdentifier($identifier) |
||
| 436 | |||
| 437 | /** |
||
| 438 | * Dispatches magic methods (findBy[Property]()) |
||
| 439 | * |
||
| 440 | * @param string $methodName The name of the magic method |
||
| 441 | * @param string $arguments The arguments of the magic method |
||
| 442 | * @throws UnsupportedMethodException |
||
| 443 | * @return mixed |
||
| 444 | * @api |
||
| 445 | */ |
||
| 446 | public function __call($methodName, $arguments) |
||
| 462 | |||
| 463 | /** |
||
| 464 | * Returns a query for objects of this repository |
||
| 465 | * |
||
| 466 | * @return Query |
||
| 467 | * @api |
||
| 468 | */ |
||
| 469 | public function createQuery() |
||
| 493 | |||
| 494 | /** |
||
| 495 | * Sets the property names to order the result by per default. |
||
| 496 | * Expected like this: |
||
| 497 | * array( |
||
| 498 | * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING, |
||
| 499 | * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING |
||
| 500 | * ) |
||
| 501 | * |
||
| 502 | * @param array $defaultOrderings The property names to order by |
||
| 503 | * @throws \BadMethodCallException |
||
| 504 | * @return void |
||
| 505 | * @api |
||
| 506 | */ |
||
| 507 | public function setDefaultOrderings(array $defaultOrderings) |
||
| 511 | |||
| 512 | /** |
||
| 513 | * Sets the default query settings to be used in this repository |
||
| 514 | * |
||
| 515 | * @param QuerySettingsInterface $defaultQuerySettings The query settings to be used by default |
||
| 516 | * @throws \BadMethodCallException |
||
| 517 | * @return void |
||
| 518 | * @api |
||
| 519 | */ |
||
| 520 | public function setDefaultQuerySettings(QuerySettingsInterface $defaultQuerySettings) |
||
| 524 | |||
| 525 | /** |
||
| 526 | * @return void |
||
| 527 | */ |
||
| 528 | public function resetDefaultQuerySettings() |
||
| 532 | |||
| 533 | |||
| 534 | /** |
||
| 535 | * @return array |
||
| 536 | */ |
||
| 537 | public function getErrorMessages() |
||
| 541 | |||
| 542 | /** |
||
| 543 | * @param string $sourceFieldName |
||
| 544 | * @return $this |
||
| 545 | */ |
||
| 546 | public function setSourceFieldName($sourceFieldName) |
||
| 551 | |||
| 552 | /** |
||
| 553 | * @return string |
||
| 554 | */ |
||
| 555 | public function getDataType() |
||
| 559 | |||
| 560 | /** |
||
| 561 | * Tell whether the order has a foreign table in its expression, e.g. "metadata.title". |
||
| 562 | * |
||
| 563 | * @param string $ordering |
||
| 564 | * @return bool |
||
| 565 | */ |
||
| 566 | protected function hasForeignRelationIn($ordering) |
||
| 570 | |||
| 571 | /** |
||
| 572 | * Extract the foreign relation of the ordering "metadata.title" -> "metadata" |
||
| 573 | * |
||
| 574 | * @param string $ordering |
||
| 575 | * @return string |
||
| 576 | */ |
||
| 577 | protected function getForeignRelationFrom($ordering) |
||
| 582 | |||
| 583 | /** |
||
| 584 | * Get the constraints |
||
| 585 | * |
||
| 586 | * @param Query $query |
||
| 587 | * @param Matcher $matcher |
||
| 588 | * @return ConstraintInterface|null |
||
| 589 | */ |
||
| 590 | protected function computeConstraints(Query $query, Matcher $matcher) |
||
| 624 | |||
| 625 | /** |
||
| 626 | * Computes the search constraint and returns it. |
||
| 627 | * |
||
| 628 | * @param Query $query |
||
| 629 | * @param Matcher $matcher |
||
| 630 | * @return ConstraintInterface|null |
||
| 631 | */ |
||
| 632 | protected function computeSearchTermConstraint(Query $query, Matcher $matcher) |
||
| 663 | |||
| 664 | /** |
||
| 665 | * It does not make sense to have a "like" in presence of numerical field, e.g "uid". |
||
| 666 | * Tell whether the given value makes sense for a "like" clause. |
||
| 667 | * |
||
| 668 | * @param string $fieldNameAndPath |
||
| 669 | * @param string $value |
||
| 670 | * @return bool |
||
| 671 | */ |
||
| 672 | protected function isSuitableForLike($fieldNameAndPath, $value) |
||
| 691 | |||
| 692 | /** |
||
| 693 | * Computes the constraint for matches and returns it. |
||
| 694 | * |
||
| 695 | * @param Query $query |
||
| 696 | * @param Matcher $matcher |
||
| 697 | * @param string $operator |
||
| 698 | * @return ConstraintInterface|null |
||
| 699 | */ |
||
| 700 | protected function computeConstraint(Query $query, Matcher $matcher, $operator) |
||
| 748 | |||
| 749 | /** |
||
| 750 | * @return DataHandler |
||
| 751 | */ |
||
| 752 | protected function getDataHandler() |
||
| 759 | |||
| 760 | /** |
||
| 761 | * Handle the magic call by properly creating a Query object and returning its result. |
||
| 762 | * |
||
| 763 | * @param string $propertyName |
||
| 764 | * @param string $value |
||
| 765 | * @param string $flag |
||
| 766 | * @return array |
||
| 767 | */ |
||
| 768 | protected function processMagicCall($propertyName, $value, $flag = '') |
||
| 793 | |||
| 794 | /** |
||
| 795 | * @return DataHandlerFactory |
||
| 796 | */ |
||
| 797 | protected function getDataHandlerFactory() |
||
| 801 | |||
| 802 | /** |
||
| 803 | * Returns whether the current mode is Backend |
||
| 804 | * |
||
| 805 | * @return bool |
||
| 806 | */ |
||
| 807 | protected function isBackendMode() |
||
| 811 | |||
| 812 | /** |
||
| 813 | * @return FieldPathResolver |
||
| 814 | */ |
||
| 815 | protected function getFieldPathResolver() |
||
| 819 | |||
| 820 | /** |
||
| 821 | * @return ObjectManager |
||
| 822 | */ |
||
| 823 | protected function getObjectManager() |
||
| 827 | |||
| 828 | /** |
||
| 829 | * @return ContentValidator |
||
| 830 | */ |
||
| 831 | protected function getContentValidator() |
||
| 835 | |||
| 836 | /** |
||
| 837 | * @return LanguageValidator |
||
| 838 | */ |
||
| 839 | protected function getLanguageValidator() |
||
| 843 | |||
| 844 | /** |
||
| 845 | * Signal that is called for post-processing the computed constraints object. |
||
| 846 | * |
||
| 847 | * @param Query $query |
||
| 848 | * @param ConstraintInterface|null $constraints |
||
| 849 | * @return ConstraintInterface|null $constraints |
||
| 850 | * @signal |
||
| 851 | */ |
||
| 852 | protected function emitPostProcessConstraintsSignal(Query $query, $constraints) |
||
| 865 | |||
| 866 | /** |
||
| 867 | * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher |
||
| 868 | */ |
||
| 869 | protected function getSignalSlotDispatcher() |
||
| 873 | |||
| 874 | } |
||
| 875 |
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.