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 EntryManager 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 EntryManager, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 15 | class EntryManager |
||
| 16 | { |
||
| 17 | /** |
||
| 18 | * The Field ID that will be used to sort when fetching Entries, defaults |
||
| 19 | * to null, which implies the Entry ID (id column in `tbl_entries`) |
||
| 20 | * @var integer |
||
| 21 | */ |
||
| 22 | protected static $_fetchSortField = null; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * The direction that entries should be sorted in, available options are |
||
| 26 | * RAND, ASC or DESC. Defaults to null, which implies ASC |
||
| 27 | * @var string |
||
| 28 | */ |
||
| 29 | protected static $_fetchSortDirection = null; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * Setter function for the default sorting direction of the Fetch |
||
| 33 | * function. Available options are RAND, ASC or DESC. |
||
| 34 | * |
||
| 35 | * @param string $direction |
||
| 36 | * The direction that entries should be sorted in, available options |
||
| 37 | * are RAND, ASC or DESC. |
||
| 38 | */ |
||
| 39 | public static function setFetchSortingDirection($direction) |
||
| 49 | |||
| 50 | /** |
||
| 51 | * Sets the field to applying the sorting direction on when fetching |
||
| 52 | * entries |
||
| 53 | * |
||
| 54 | * @param integer $field_id |
||
| 55 | * The ID of the Field that should be sorted on |
||
| 56 | */ |
||
| 57 | public static function setFetchSortingField($field_id) |
||
| 61 | |||
| 62 | /** |
||
| 63 | * Convenience function that will set sorting field and direction |
||
| 64 | * by calling `setFetchSortingField()` & `setFetchSortingDirection()` |
||
| 65 | * |
||
| 66 | * @see toolkit.EntryManager#setFetchSortingField() |
||
| 67 | * @see toolkit.EntryManager#setFetchSortingDirection() |
||
| 68 | * @param integer $field_id |
||
| 69 | * The ID of the Field that should be sorted on |
||
| 70 | * @param string $direction |
||
| 71 | * The direction that entries should be sorted in, available options |
||
| 72 | * are RAND, ASC or DESC. Defaults to ASC |
||
| 73 | */ |
||
| 74 | public static function setFetchSorting($field_id, $direction = 'ASC') |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Returns an object representation of the sorting for the |
||
| 82 | * EntryManager, with the field and direction provided |
||
| 83 | * |
||
| 84 | * @return StdClass |
||
| 85 | */ |
||
| 86 | public static function getFetchSorting() |
||
| 93 | |||
| 94 | /** |
||
| 95 | * Given an Entry object, iterate over all of the fields in that object |
||
| 96 | * an insert them into their relevant entry tables. |
||
| 97 | * |
||
| 98 | * @param Entry $entry |
||
| 99 | * An Entry object to insert into the database |
||
| 100 | * @throws DatabaseException |
||
| 101 | * @return boolean |
||
| 102 | */ |
||
| 103 | public static function add(Entry $entry) |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Update an existing Entry object given an Entry object |
||
| 150 | * |
||
| 151 | * @param Entry $entry |
||
| 152 | * An Entry object |
||
| 153 | * @throws DatabaseException |
||
| 154 | * @return boolean |
||
| 155 | */ |
||
| 156 | public static function edit(Entry $entry) |
||
| 222 | |||
| 223 | /** |
||
| 224 | * Given an Entry ID, or an array of Entry ID's, delete all |
||
| 225 | * data associated with this Entry using a Field's `entryDataCleanup()` |
||
| 226 | * function, and then remove this Entry from `tbl_entries`. If the `$entries` |
||
| 227 | * all belong to the same section, passing `$section_id` will improve |
||
| 228 | * performance |
||
| 229 | * |
||
| 230 | * @param array|integer $entries |
||
| 231 | * An entry_id, or an array of entry id's to delete |
||
| 232 | * @param integer $section_id (optional) |
||
| 233 | * If possible, the `$section_id` of the the `$entries`. This parameter |
||
| 234 | * should be left as null if the `$entries` array contains entry_id's for |
||
| 235 | * multiple sections. |
||
| 236 | * @throws DatabaseException |
||
| 237 | * @throws Exception |
||
| 238 | * @return boolean |
||
| 239 | */ |
||
| 240 | public static function delete($entries, $section_id = null) |
||
| 330 | |||
| 331 | /** |
||
| 332 | * This function will return an array of Entry objects given an ID or an array of ID's. |
||
| 333 | * Do not provide `$entry_id` as an array if not specifying the `$section_id`. This function |
||
| 334 | * is commonly passed custom SQL statements through the `$where` and `$join` parameters |
||
| 335 | * that is generated by the fields of this section |
||
| 336 | * |
||
| 337 | * @param integer|array $entry_id |
||
| 338 | * An array of Entry ID's or an Entry ID to return |
||
| 339 | * @param integer $section_id |
||
| 340 | * The ID of the Section that these entries are contained in |
||
| 341 | * @param integer $limit |
||
| 342 | * The limit of entries to return |
||
| 343 | * @param integer $start |
||
| 344 | * The starting offset of the entries to return |
||
| 345 | * @param string $where |
||
| 346 | * Any custom WHERE clauses. The tbl_entries alias is `e` |
||
| 347 | * @param string $joins |
||
| 348 | * Any custom JOIN's |
||
| 349 | * @param boolean $group |
||
| 350 | * Whether the entries need to be grouped by Entry ID or not |
||
| 351 | * @param boolean $buildentries |
||
| 352 | * Whether to return an array of entry ID's or Entry objects. Defaults to |
||
| 353 | * true, which will return Entry objects |
||
| 354 | * @param array $element_names |
||
| 355 | * Choose whether to get data from a subset of fields or all fields in a section, |
||
| 356 | * by providing an array of field names. Defaults to null, which will load data |
||
| 357 | * from all fields in a section. |
||
| 358 | * @param boolean $enable_sort |
||
| 359 | * Defaults to true, if false this function will not apply any sorting |
||
| 360 | * @throws Exception |
||
| 361 | * @return array |
||
| 362 | * If `$buildentries` is true, this function will return an array of Entry objects, |
||
| 363 | * otherwise it will return an associative array of Entry data from `tbl_entries` |
||
| 364 | */ |
||
| 365 | public static function fetch($entry_id = null, $section_id = null, $limit = null, $start = null, $where = null, $joins = null, $group = false, $buildentries = true, $element_names = null, $enable_sort = true) |
||
| 466 | |||
| 467 | /** |
||
| 468 | * Given an array of Entry data from `tbl_entries` and a section ID, return an |
||
| 469 | * array of Entry objects. For performance reasons, it's possible to pass an array |
||
| 470 | * of field handles via `$element_names`, so that only a subset of the section schema |
||
| 471 | * will be queried. This function currently only supports Entry from one section at a |
||
| 472 | * time. |
||
| 473 | * |
||
| 474 | * @param array $rows |
||
| 475 | * An array of Entry data from `tbl_entries` including the Entry ID, Entry section, |
||
| 476 | * the ID of the Author who created the Entry, and a Unix timestamp of creation |
||
| 477 | * @param integer $section_id |
||
| 478 | * The section ID of the entries in the `$rows` |
||
| 479 | * @param array $element_names |
||
| 480 | * Choose whether to get data from a subset of fields or all fields in a section, |
||
| 481 | * by providing an array of field names. Defaults to null, which will load data |
||
| 482 | * from all fields in a section. |
||
| 483 | * @throws DatabaseException |
||
| 484 | * @return array |
||
| 485 | * An array of Entry objects |
||
| 486 | */ |
||
| 487 | public static function __buildEntries(array $rows, $section_id, $element_names = null) |
||
| 593 | |||
| 594 | |||
| 595 | /** |
||
| 596 | * Given an Entry ID, return the Section ID that it belongs to |
||
| 597 | * |
||
| 598 | * @param integer $entry_id |
||
| 599 | * The ID of the Entry to return it's section |
||
| 600 | * @return integer |
||
| 601 | * The Section ID for this Entry's section |
||
| 602 | */ |
||
| 603 | public static function fetchEntrySectionID($entry_id) |
||
| 609 | |||
| 610 | /** |
||
| 611 | * Return the count of the number of entries in a particular section. |
||
| 612 | * |
||
| 613 | * @param integer $section_id |
||
| 614 | * The ID of the Section where the Entries are to be counted |
||
| 615 | * @param string $where |
||
| 616 | * Any custom WHERE clauses |
||
| 617 | * @param string $joins |
||
| 618 | * Any custom JOIN's |
||
| 619 | * @param boolean $group |
||
| 620 | * Whether the entries need to be grouped by Entry ID or not |
||
| 621 | * @return integer |
||
| 622 | */ |
||
| 623 | public static function fetchCount($section_id = null, $where = null, $joins = null, $group = false) |
||
| 648 | |||
| 649 | /** |
||
| 650 | * Returns an array of Entry objects, with some basic pagination given |
||
| 651 | * the number of Entry's to return and the current starting offset. This |
||
| 652 | * function in turn calls the fetch function that does alot of the heavy |
||
| 653 | * lifting. For instance, if there are 60 entries in a section and the pagination |
||
| 654 | * dictates that per page, 15 entries are to be returned, by passing 2 to |
||
| 655 | * the $page parameter you could return entries 15-30 |
||
| 656 | * |
||
| 657 | * @param integer $page |
||
| 658 | * The page to return, defaults to 1 |
||
| 659 | * @param integer $section_id |
||
| 660 | * The ID of the Section that these entries are contained in |
||
| 661 | * @param integer $entriesPerPage |
||
| 662 | * The number of entries to return per page. |
||
| 663 | * @param string $where |
||
| 664 | * Any custom WHERE clauses |
||
| 665 | * @param string $joins |
||
| 666 | * Any custom JOIN's |
||
| 667 | * @param boolean $group |
||
| 668 | * Whether the entries need to be grouped by Entry ID or not |
||
| 669 | * @param boolean $records_only |
||
| 670 | * If this is set to true, an array of Entry objects will be returned |
||
| 671 | * without any basic pagination information. Defaults to false |
||
| 672 | * @param boolean $buildentries |
||
| 673 | * Whether to return an array of entry ID's or Entry objects. Defaults to |
||
| 674 | * true, which will return Entry objects |
||
| 675 | * @param array $element_names |
||
| 676 | * Choose whether to get data from a subset of fields or all fields in a section, |
||
| 677 | * by providing an array of field names. Defaults to null, which will load data |
||
| 678 | * from all fields in a section. |
||
| 679 | * @throws Exception |
||
| 680 | * @return array |
||
| 681 | * Either an array of Entry objects, or an associative array containing |
||
| 682 | * the total entries, the start position, the entries per page and the |
||
| 683 | * Entry objects |
||
| 684 | */ |
||
| 685 | public static function fetchByPage($page = 1, $section_id, $entriesPerPage, $where = null, $joins = null, $group = false, $records_only = false, $buildentries = true, array $element_names = null) |
||
| 728 | |||
| 729 | /** |
||
| 730 | * Creates a new Entry object using this class as the parent. |
||
| 731 | * |
||
| 732 | * @return Entry |
||
| 733 | */ |
||
| 734 | public static function create() |
||
| 738 | } |
||
| 739 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.