Complex classes like SearchIndex 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 SearchIndex, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
46 | abstract class SearchIndex extends ViewableData |
||
47 | { |
||
48 | /** |
||
49 | * Allows this index to hide a parent index. Specifies the name of a parent index to disable |
||
50 | * |
||
51 | * @var string |
||
52 | * @config |
||
53 | */ |
||
54 | private static $hide_ancestor; |
||
|
|||
55 | |||
56 | /** |
||
57 | * Used to separate class name and relation name in the sources array |
||
58 | * this string must not be present in class name |
||
59 | * @var string |
||
60 | * @config |
||
61 | */ |
||
62 | private static $class_delimiter = '_|_'; |
||
63 | |||
64 | /** |
||
65 | * This is used to clean the source name from suffix |
||
66 | * suffixes are needed to support multiple relations with the same name on different page types |
||
67 | * @param string $source |
||
68 | * @return string |
||
69 | */ |
||
70 | protected function getSourceName($source) |
||
71 | { |
||
72 | $source = explode(self::config()->get('class_delimiter'), $source); |
||
73 | |||
74 | return $source[0]; |
||
75 | } |
||
76 | |||
77 | public function __construct() |
||
78 | { |
||
79 | parent::__construct(); |
||
80 | $this->init(); |
||
81 | |||
82 | foreach ($this->getClasses() as $class => $options) { |
||
83 | SearchVariant::with($class, $options['include_children'])->call('alterDefinition', $class, $this); |
||
84 | } |
||
85 | |||
86 | $this->buildDependancyList(); |
||
87 | } |
||
88 | |||
89 | public function __toString() |
||
90 | { |
||
91 | return 'Search Index ' . get_class($this); |
||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Examines the classes this index is built on to try and find defined fields in the class hierarchy |
||
96 | * for those classes. |
||
97 | * Looks for db and viewable-data fields, although can't necessarily find type for viewable-data fields. |
||
98 | * If multiple classes have a relation with the same name all of these will be included in the search index |
||
99 | * Note that only classes that have the relations uninherited (defined in them) will be listed |
||
100 | * this is because inherited relations do not need to be processed by index explicitly |
||
101 | */ |
||
102 | public function fieldData($field, $forceType = null, $extraOptions = []) |
||
256 | |||
257 | /** Public, but should only be altered by variants */ |
||
258 | |||
259 | protected $classes = array(); |
||
260 | |||
261 | protected $fulltextFields = array(); |
||
262 | |||
263 | public $filterFields = array(); |
||
264 | |||
265 | protected $sortFields = array(); |
||
266 | |||
267 | protected $excludedVariantStates = array(); |
||
268 | |||
269 | /** |
||
270 | * Add a DataObject subclass whose instances should be included in this index |
||
271 | * |
||
272 | * Can only be called when addFulltextField, addFilterField, addSortField and addAllFulltextFields have not |
||
273 | * yet been called for this index instance |
||
274 | * |
||
275 | * @throws Exception |
||
276 | * @param string $class - The class to include |
||
277 | * @param array $options - TODO: Remove |
||
278 | */ |
||
279 | public function addClass($class, $options = array()) |
||
291 | |||
292 | /** |
||
293 | * Get the classes added by addClass |
||
294 | */ |
||
295 | public function getClasses() |
||
299 | |||
300 | /** |
||
301 | * Add a field that should be fulltext searchable |
||
302 | * @param string $field - The field to add |
||
303 | * @param string $forceType - The type to force this field as (required in some cases, when not detectable from metadata) |
||
304 | * @param string $extraOptions - Dependent on search implementation |
||
305 | */ |
||
306 | public function addFulltextField($field, $forceType = null, $extraOptions = array()) |
||
310 | |||
311 | public function getFulltextFields() |
||
315 | |||
316 | /** |
||
317 | * Add a field that should be filterable |
||
318 | * @param string $field - The field to add |
||
319 | * @param string $forceType - The type to force this field as (required in some cases, when not detectable from metadata) |
||
320 | * @param string $extraOptions - Dependent on search implementation |
||
321 | */ |
||
322 | public function addFilterField($field, $forceType = null, $extraOptions = array()) |
||
326 | |||
327 | public function getFilterFields() |
||
331 | |||
332 | /** |
||
333 | * Add a field that should be sortable |
||
334 | * @param string $field - The field to add |
||
335 | * @param string $forceType - The type to force this field as (required in some cases, when not detectable from metadata) |
||
336 | * @param string $extraOptions - Dependent on search implementation |
||
337 | */ |
||
338 | public function addSortField($field, $forceType = null, $extraOptions = array()) |
||
342 | |||
343 | public function getSortFields() |
||
347 | |||
348 | /** |
||
349 | * Add all database-backed text fields as fulltext searchable fields. |
||
350 | * |
||
351 | * For every class included in the index, examines those classes and all subclasses looking for "Text" database |
||
352 | * fields (Varchar, Text, HTMLText, etc) and adds them all as fulltext searchable fields. |
||
353 | */ |
||
354 | public function addAllFulltextFields($includeSubclasses = true) |
||
374 | |||
375 | /** |
||
376 | * Returns an interator that will let you interate through all added fields, regardless of whether they |
||
377 | * were added as fulltext, filter or sort fields. |
||
378 | * |
||
379 | * @return MultipleArrayIterator |
||
380 | */ |
||
381 | public function getFieldsIterator() |
||
385 | |||
386 | public function excludeVariantState($state) |
||
390 | |||
391 | /** Returns true if some variant state should be ignored */ |
||
392 | public function variantStateExcluded($state) |
||
409 | |||
410 | public $dependancyList = array(); |
||
411 | |||
412 | public function buildDependancyList() |
||
423 | |||
424 | public $derivedFields = null; |
||
425 | |||
426 | /** |
||
427 | * Returns an array where each member is all the fields and the classes that are at the end of some |
||
428 | * specific lookup chain from one of the base classes |
||
429 | */ |
||
430 | public function getDerivedFields() |
||
462 | |||
463 | /** |
||
464 | * Get the "document ID" (a database & variant unique id) given some "Base" class, DataObject ID and state array |
||
465 | * |
||
466 | * @param string $base - The base class of the object |
||
467 | * @param integer $id - The ID of the object |
||
468 | * @param array $state - The variant state of the object |
||
469 | * @return string - The document ID as a string |
||
470 | */ |
||
471 | public function getDocumentIDForState($base, $id, $state) |
||
477 | |||
478 | /** |
||
479 | * Get the "document ID" (a database & variant unique id) given some "Base" class and DataObject |
||
480 | * |
||
481 | * @param DataObject $object - The object |
||
482 | * @param string $base - The base class of the object |
||
483 | * @param boolean $includesubs - TODO: Probably going away |
||
484 | * @return string - The document ID as a string |
||
485 | */ |
||
486 | public function getDocumentID($object, $base, $includesubs) |
||
490 | |||
491 | /** |
||
492 | * Given an object and a field definition (as returned by fieldData) get the current value of that field on that object |
||
493 | * |
||
494 | * @param DataObject $object - The object to get the value from |
||
495 | * @param array $field - The field definition to use |
||
496 | * @return mixed - The value of the field, or null if we couldn't look it up for some reason |
||
497 | */ |
||
498 | protected function _getFieldValue($object, $field) |
||
559 | |||
560 | /** |
||
561 | * Log non-fatal errors |
||
562 | * |
||
563 | * @param Exception $e |
||
564 | */ |
||
565 | public static function warn($e) |
||
569 | |||
570 | /** |
||
571 | * Given a class, object id, set of stateful ids and a list of changed fields (in a special format), |
||
572 | * return what statefulids need updating in this index |
||
573 | * |
||
574 | * Internal function used by SearchUpdater. |
||
575 | * |
||
576 | * @param string $class |
||
577 | * @param int $id |
||
578 | * @param array $statefulids |
||
579 | * @param array $fields |
||
580 | * @return array |
||
581 | */ |
||
582 | public function getDirtyIDs($class, $id, $statefulids, $fields) |
||
660 | |||
661 | /** !! These should be implemented by the full text search engine */ |
||
662 | |||
663 | abstract public function add($object); |
||
665 | |||
666 | abstract public function commit(); |
||
667 | |||
668 | /** !! These should be implemented by the specific index */ |
||
669 | |||
670 | /** |
||
671 | * Called during construction, this is the method that builds the structure. |
||
672 | * Used instead of overriding __construct as we have specific execution order - code that has |
||
673 | * to be run before _and/or_ after this. |
||
674 | */ |
||
675 | abstract public function init(); |
||
676 | } |
||
677 |
This check marks private properties in classes that are never used. Those properties can be removed.