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 EditableFormField 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 EditableFormField, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 61 | class EditableFormField extends DataObject  | 
            ||
| 62 | { | 
            ||
| 63 | /**  | 
            ||
| 64 | * Set to true to hide from class selector  | 
            ||
| 65 | *  | 
            ||
| 66 | * @config  | 
            ||
| 67 | * @var bool  | 
            ||
| 68 | */  | 
            ||
| 69 | private static $hidden = false;  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 70 | |||
| 71 | /**  | 
            ||
| 72 | * Define this field as abstract (not inherited)  | 
            ||
| 73 | *  | 
            ||
| 74 | * @config  | 
            ||
| 75 | * @var bool  | 
            ||
| 76 | */  | 
            ||
| 77 | private static $abstract = true;  | 
            ||
| 78 | |||
| 79 | /**  | 
            ||
| 80 | * Flag this field type as non-data (e.g. literal, header, html)  | 
            ||
| 81 | *  | 
            ||
| 82 | * @config  | 
            ||
| 83 | * @var bool  | 
            ||
| 84 | */  | 
            ||
| 85 | private static $literal = false;  | 
            ||
| 86 | |||
| 87 | /**  | 
            ||
| 88 | * Default sort order  | 
            ||
| 89 | *  | 
            ||
| 90 | * @config  | 
            ||
| 91 | * @var string  | 
            ||
| 92 | */  | 
            ||
| 93 | private static $default_sort = '"Sort"';  | 
            ||
| 94 | |||
| 95 | /**  | 
            ||
| 96 | * A list of CSS classes that can be added  | 
            ||
| 97 | *  | 
            ||
| 98 | * @var array  | 
            ||
| 99 | */  | 
            ||
| 100 | public static $allowed_css = [];  | 
            ||
| 101 | |||
| 102 | /**  | 
            ||
| 103 | * Set this to true to enable placeholder field for any given class  | 
            ||
| 104 | * @config  | 
            ||
| 105 | * @var bool  | 
            ||
| 106 | */  | 
            ||
| 107 | private static $has_placeholder = false;  | 
            ||
| 108 | |||
| 109 | /**  | 
            ||
| 110 | * @config  | 
            ||
| 111 | * @var array  | 
            ||
| 112 | */  | 
            ||
| 113 | private static $summary_fields = [  | 
            ||
| 114 | 'Title'  | 
            ||
| 115 | ];  | 
            ||
| 116 | |||
| 117 | /**  | 
            ||
| 118 | * @config  | 
            ||
| 119 | * @var array  | 
            ||
| 120 | */  | 
            ||
| 121 | private static $db = [  | 
            ||
| 122 | 'Name' => 'Varchar',  | 
            ||
| 123 | 'Title' => 'Varchar(255)',  | 
            ||
| 124 | 'Default' => 'Varchar(255)',  | 
            ||
| 125 | 'Sort' => 'Int',  | 
            ||
| 126 | 'Required' => 'Boolean',  | 
            ||
| 127 | 'CustomErrorMessage' => 'Varchar(255)',  | 
            ||
| 128 | 'ExtraClass' => 'Text',  | 
            ||
| 129 | 'RightTitle' => 'Varchar(255)',  | 
            ||
| 130 | 'ShowOnLoad' => 'Boolean(1)',  | 
            ||
| 131 | 'ShowInSummary' => 'Boolean',  | 
            ||
| 132 | 'Placeholder' => 'Varchar(255)',  | 
            ||
| 133 |         'DisplayRulesConjunction' => 'Enum("And,Or","Or")', | 
            ||
| 134 | ];  | 
            ||
| 135 | |||
| 136 | private static $table_name = 'EditableFormField';  | 
            ||
| 137 | |||
| 138 | |||
| 139 | private static $defaults = [  | 
            ||
| 140 | 'ShowOnLoad' => true,  | 
            ||
| 141 | ];  | 
            ||
| 142 | |||
| 143 | |||
| 144 | /**  | 
            ||
| 145 | * @config  | 
            ||
| 146 | * @var array  | 
            ||
| 147 | */  | 
            ||
| 148 | private static $has_one = [  | 
            ||
| 149 | 'Parent' => UserDefinedForm::class,  | 
            ||
| 150 | ];  | 
            ||
| 151 | |||
| 152 | /**  | 
            ||
| 153 | * Built in extensions required  | 
            ||
| 154 | *  | 
            ||
| 155 | * @config  | 
            ||
| 156 | * @var array  | 
            ||
| 157 | */  | 
            ||
| 158 | private static $extensions = [  | 
            ||
| 159 |         Versioned::class . "('Stage', 'Live')" | 
            ||
| 160 | ];  | 
            ||
| 161 | |||
| 162 | /**  | 
            ||
| 163 | * @config  | 
            ||
| 164 | * @var array  | 
            ||
| 165 | */  | 
            ||
| 166 | private static $has_many = [  | 
            ||
| 167 | 'DisplayRules' => EditableCustomRule::class . '.Parent'  | 
            ||
| 168 | ];  | 
            ||
| 169 | |||
| 170 | private static $owns = [  | 
            ||
| 171 | 'DisplayRules',  | 
            ||
| 172 | ];  | 
            ||
| 173 | |||
| 174 | private static $cascade_deletes = [  | 
            ||
| 175 | 'DisplayRules',  | 
            ||
| 176 | ];  | 
            ||
| 177 | |||
| 178 | /**  | 
            ||
| 179 | * @var bool  | 
            ||
| 180 | */  | 
            ||
| 181 | protected $readonly;  | 
            ||
| 182 | |||
| 183 | /**  | 
            ||
| 184 | * Property holds the JS event which gets fired for this type of element  | 
            ||
| 185 | *  | 
            ||
| 186 | * @var string  | 
            ||
| 187 | */  | 
            ||
| 188 | protected $jsEventHandler = 'change';  | 
            ||
| 189 | |||
| 190 | /**  | 
            ||
| 191 | * Returns the jsEventHandler property for the current object. Bearing in mind it could've been overridden.  | 
            ||
| 192 | * @return string  | 
            ||
| 193 | */  | 
            ||
| 194 | public function getJsEventHandler()  | 
            ||
| 198 | |||
| 199 | /**  | 
            ||
| 200 | * Set the visibility of an individual form field  | 
            ||
| 201 | *  | 
            ||
| 202 | * @param bool  | 
            ||
| 203 | * @return $this  | 
            ||
| 204 | */  | 
            ||
| 205 | public function setReadonly($readonly = true)  | 
            ||
| 210 | |||
| 211 | /**  | 
            ||
| 212 | * Returns whether this field is readonly  | 
            ||
| 213 | *  | 
            ||
| 214 | * @return bool  | 
            ||
| 215 | */  | 
            ||
| 216 | private function isReadonly()  | 
            ||
| 220 | |||
| 221 | /**  | 
            ||
| 222 | * @return FieldList  | 
            ||
| 223 | */  | 
            ||
| 224 | public function getCMSFields()  | 
            ||
| 324 | |||
| 325 | /**  | 
            ||
| 326 | * Return fields to display on the 'Display Rules' tab  | 
            ||
| 327 | *  | 
            ||
| 328 | * @return FieldList  | 
            ||
| 329 | */  | 
            ||
| 330 | protected function getDisplayRuleFields()  | 
            ||
| 401 | |||
| 402 | public function onBeforeWrite()  | 
            ||
| 421 | |||
| 422 | /**  | 
            ||
| 423 | * Generate a new non-conflicting Name value  | 
            ||
| 424 | *  | 
            ||
| 425 | * @return string  | 
            ||
| 426 | */  | 
            ||
| 427 | protected function generateName()  | 
            ||
| 441 | |||
| 442 | /**  | 
            ||
| 443 | * Flag indicating that this field will set its own error message via data-msg='' attributes  | 
            ||
| 444 | *  | 
            ||
| 445 | * @return bool  | 
            ||
| 446 | */  | 
            ||
| 447 | public function getSetsOwnError()  | 
            ||
| 451 | |||
| 452 | /**  | 
            ||
| 453 | * Return whether a user can delete this form field  | 
            ||
| 454 | * based on whether they can edit the page  | 
            ||
| 455 | *  | 
            ||
| 456 | * @param Member $member  | 
            ||
| 457 | * @return bool  | 
            ||
| 458 | */  | 
            ||
| 459 | public function canDelete($member = null)  | 
            ||
| 463 | |||
| 464 | /**  | 
            ||
| 465 | * Return whether a user can edit this form field  | 
            ||
| 466 | * based on whether they can edit the page  | 
            ||
| 467 | *  | 
            ||
| 468 | * @param Member $member  | 
            ||
| 469 | * @return bool  | 
            ||
| 470 | */  | 
            ||
| 471 | public function canEdit($member = null)  | 
            ||
| 495 | |||
| 496 | /**  | 
            ||
| 497 | * Return whether a user can view this form field  | 
            ||
| 498 | * based on whether they can view the page, regardless of the ReadOnly status of the field  | 
            ||
| 499 | *  | 
            ||
| 500 | * @param Member $member  | 
            ||
| 501 | * @return bool  | 
            ||
| 502 | */  | 
            ||
| 503 | public function canView($member = null)  | 
            ||
| 512 | |||
| 513 | /**  | 
            ||
| 514 | * Return whether a user can create an object of this type  | 
            ||
| 515 | *  | 
            ||
| 516 | * @param Member $member  | 
            ||
| 517 | * @param array $context Virtual parameter to allow context to be passed in to check  | 
            ||
| 518 | * @return bool  | 
            ||
| 519 | */  | 
            ||
| 520 | View Code Duplication | public function canCreate($member = null, $context = [])  | 
            |
| 531 | |||
| 532 | /**  | 
            ||
| 533 | * Helper method to check the parent for this object  | 
            ||
| 534 | *  | 
            ||
| 535 | * @param array $args List of arguments passed to canCreate  | 
            ||
| 536 | * @return SiteTree Parent page instance  | 
            ||
| 537 | */  | 
            ||
| 538 | View Code Duplication | protected function getCanCreateContext($args)  | 
            |
| 552 | |||
| 553 | /**  | 
            ||
| 554 | * checks whether record is new, copied from SiteTree  | 
            ||
| 555 | */  | 
            ||
| 556 | public function isNew()  | 
            ||
| 568 | |||
| 569 | /**  | 
            ||
| 570 | * Set the allowed css classes for the extraClass custom setting  | 
            ||
| 571 | *  | 
            ||
| 572 | * @param array $allowed The permissible CSS classes to add  | 
            ||
| 573 | */  | 
            ||
| 574 | public function setAllowedCss(array $allowed)  | 
            ||
| 582 | |||
| 583 | /**  | 
            ||
| 584 | * Get the path to the icon for this field type, relative to the site root.  | 
            ||
| 585 | *  | 
            ||
| 586 | * @return string  | 
            ||
| 587 | */  | 
            ||
| 588 | public function getIcon()  | 
            ||
| 593 | |||
| 594 | /**  | 
            ||
| 595 | * Return whether or not this field has addable options  | 
            ||
| 596 | * such as a dropdown field or radio set  | 
            ||
| 597 | *  | 
            ||
| 598 | * @return bool  | 
            ||
| 599 | */  | 
            ||
| 600 | public function getHasAddableOptions()  | 
            ||
| 604 | |||
| 605 | /**  | 
            ||
| 606 | * Return whether or not this field needs to show the extra  | 
            ||
| 607 | * options dropdown list  | 
            ||
| 608 | *  | 
            ||
| 609 | * @return bool  | 
            ||
| 610 | */  | 
            ||
| 611 | public function showExtraOptions()  | 
            ||
| 615 | |||
| 616 | /**  | 
            ||
| 617 | * Returns the Title for rendering in the front-end (with XML values escaped)  | 
            ||
| 618 | *  | 
            ||
| 619 | * @return string  | 
            ||
| 620 | */  | 
            ||
| 621 | public function getEscapedTitle()  | 
            ||
| 625 | |||
| 626 | /**  | 
            ||
| 627 | * Find the numeric indicator (1.1.2) that represents it's nesting value  | 
            ||
| 628 | *  | 
            ||
| 629 | * Only useful for fields attached to a current page, and that contain other fields such as pages  | 
            ||
| 630 | * or groups  | 
            ||
| 631 | *  | 
            ||
| 632 | * @return string  | 
            ||
| 633 | */  | 
            ||
| 634 | public function getFieldNumber()  | 
            ||
| 665 | |||
| 666 | public function getCMSTitle()  | 
            ||
| 670 | |||
| 671 | /**  | 
            ||
| 672 | * Append custom validation fields to the default 'Validation'  | 
            ||
| 673 | * section in the editable options view  | 
            ||
| 674 | *  | 
            ||
| 675 | * @return FieldList  | 
            ||
| 676 | */  | 
            ||
| 677 | public function getFieldValidationOptions()  | 
            ||
| 689 | |||
| 690 | /**  | 
            ||
| 691 | * Return a FormField to appear on the front end. Implement on  | 
            ||
| 692 | * your subclass.  | 
            ||
| 693 | *  | 
            ||
| 694 | * @return FormField  | 
            ||
| 695 | */  | 
            ||
| 696 | public function getFormField()  | 
            ||
| 700 | |||
| 701 | /**  | 
            ||
| 702 | * Updates a formfield with extensions  | 
            ||
| 703 | *  | 
            ||
| 704 | * @param FormField $field  | 
            ||
| 705 | */  | 
            ||
| 706 | public function doUpdateFormField($field)  | 
            ||
| 712 | |||
| 713 | /**  | 
            ||
| 714 | * Updates a formfield with the additional metadata specified by this field  | 
            ||
| 715 | *  | 
            ||
| 716 | * @param FormField $field  | 
            ||
| 717 | */  | 
            ||
| 718 | protected function updateFormField($field)  | 
            ||
| 758 | |||
| 759 | /**  | 
            ||
| 760 | * Return the instance of the submission field class  | 
            ||
| 761 | *  | 
            ||
| 762 | * @return SubmittedFormField  | 
            ||
| 763 | */  | 
            ||
| 764 | public function getSubmittedFormField()  | 
            ||
| 768 | |||
| 769 | |||
| 770 | /**  | 
            ||
| 771 | * Show this form field (and its related value) in the reports and in emails.  | 
            ||
| 772 | *  | 
            ||
| 773 | * @return bool  | 
            ||
| 774 | */  | 
            ||
| 775 | public function showInReports()  | 
            ||
| 779 | |||
| 780 | /**  | 
            ||
| 781 | * Return the error message for this field. Either uses the custom  | 
            ||
| 782 | * one (if provided) or the default SilverStripe message  | 
            ||
| 783 | *  | 
            ||
| 784 | * @return Varchar  | 
            ||
| 785 | */  | 
            ||
| 786 | public function getErrorMessage()  | 
            ||
| 796 | |||
| 797 | /**  | 
            ||
| 798 | * Invoked by UserFormUpgradeService to migrate settings specific to this field from CustomSettings  | 
            ||
| 799 | * to the field proper  | 
            ||
| 800 | *  | 
            ||
| 801 | * @param array $data Unserialised data  | 
            ||
| 802 | */  | 
            ||
| 803 | public function migrateSettings($data)  | 
            ||
| 818 | |||
| 819 | /**  | 
            ||
| 820 | * Get the formfield to use when editing this inline in gridfield  | 
            ||
| 821 | *  | 
            ||
| 822 | * @param string $column name of column  | 
            ||
| 823 | * @param array $fieldClasses List of allowed classnames if this formfield has a selectable class  | 
            ||
| 824 | * @return FormField  | 
            ||
| 825 | */  | 
            ||
| 826 | public function getInlineClassnameField($column, $fieldClasses)  | 
            ||
| 830 | |||
| 831 | /**  | 
            ||
| 832 | * Get the formfield to use when editing the title inline  | 
            ||
| 833 | *  | 
            ||
| 834 | * @param string $column  | 
            ||
| 835 | * @return FormField  | 
            ||
| 836 | */  | 
            ||
| 837 | public function getInlineTitleField($column)  | 
            ||
| 843 | |||
| 844 | /**  | 
            ||
| 845 | * Get the JS expression for selecting the holder for this field  | 
            ||
| 846 | *  | 
            ||
| 847 | * @return string  | 
            ||
| 848 | */  | 
            ||
| 849 | public function getSelectorHolder()  | 
            ||
| 853 | |||
| 854 | /**  | 
            ||
| 855 | * Returns only the JS identifier of a string, less the $(), which can be inserted elsewhere, for example when you  | 
            ||
| 856 | * want to perform selections on multiple selectors  | 
            ||
| 857 | * @return string  | 
            ||
| 858 | */  | 
            ||
| 859 | public function getSelectorOnly()  | 
            ||
| 863 | |||
| 864 | /**  | 
            ||
| 865 | * Gets the JS expression for selecting the value for this field  | 
            ||
| 866 | *  | 
            ||
| 867 | * @param EditableCustomRule $rule Custom rule this selector will be used with  | 
            ||
| 868 | * @param bool $forOnLoad Set to true if this will be invoked on load  | 
            ||
| 869 | *  | 
            ||
| 870 | * @return string  | 
            ||
| 871 | */  | 
            ||
| 872 | public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)  | 
            ||
| 876 | |||
| 877 | /**  | 
            ||
| 878 | * @return string  | 
            ||
| 879 | */  | 
            ||
| 880 | public function getSelectorFieldOnly()  | 
            ||
| 884 | |||
| 885 | |||
| 886 | /**  | 
            ||
| 887 | * Get the list of classes that can be selected and used as data-values  | 
            ||
| 888 | *  | 
            ||
| 889 | * @param $includeLiterals Set to false to exclude non-data fields  | 
            ||
| 890 | * @return array  | 
            ||
| 891 | */  | 
            ||
| 892 | public function getEditableFieldClasses($includeLiterals = true)  | 
            ||
| 921 | |||
| 922 | /**  | 
            ||
| 923 | * @return EditableFormField\Validator  | 
            ||
| 924 | */  | 
            ||
| 925 | public function getCMSValidator()  | 
            ||
| 930 | |||
| 931 | /**  | 
            ||
| 932 | * Determine effective display rules for this field.  | 
            ||
| 933 | *  | 
            ||
| 934 | * @return SS_List  | 
            ||
| 935 | */  | 
            ||
| 936 | public function EffectiveDisplayRules()  | 
            ||
| 943 | |||
| 944 | /**  | 
            ||
| 945 | * Extracts info from DisplayRules into array so UserDefinedForm->buildWatchJS can run through it.  | 
            ||
| 946 | * @return array|null  | 
            ||
| 947 | */  | 
            ||
| 948 | public function formatDisplayRules()  | 
            ||
| 991 | |||
| 992 | /**  | 
            ||
| 993 | * Replaces the set DisplayRulesConjunction with their JS logical operators  | 
            ||
| 994 | * @return string  | 
            ||
| 995 | */  | 
            ||
| 996 | public function DisplayRulesConjunctionNice()  | 
            ||
| 1000 | |||
| 1001 | /**  | 
            ||
| 1002 | * Replaces boolean ShowOnLoad with its JS string equivalent  | 
            ||
| 1003 | * @return string  | 
            ||
| 1004 | */  | 
            ||
| 1005 | public function ShowOnLoadNice()  | 
            ||
| 1009 | |||
| 1010 | /**  | 
            ||
| 1011 | * Returns whether this is of type EditableCheckBoxField  | 
            ||
| 1012 | * @return bool  | 
            ||
| 1013 | */  | 
            ||
| 1014 | public function isCheckBoxField()  | 
            ||
| 1018 | |||
| 1019 | /**  | 
            ||
| 1020 | * Returns whether this is of type EditableRadioField  | 
            ||
| 1021 | * @return bool  | 
            ||
| 1022 | */  | 
            ||
| 1023 | public function isRadioField()  | 
            ||
| 1027 | |||
| 1028 | /**  | 
            ||
| 1029 | * Determined is this is of type EditableCheckboxGroupField  | 
            ||
| 1030 | * @return bool  | 
            ||
| 1031 | */  | 
            ||
| 1032 | public function isCheckBoxGroupField()  | 
            ||
| 1036 | }  | 
            ||
| 1037 | 
This check marks private properties in classes that are never used. Those properties can be removed.