Complex classes like Indexer 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 Indexer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
50 | class Indexer extends AbstractIndexer |
||
51 | { |
||
52 | |||
53 | # TODO change to singular $document instead of plural $documents |
||
54 | |||
55 | /** |
||
56 | * A Solr service instance to interact with the Solr server |
||
57 | * |
||
58 | * @var SolrService |
||
59 | */ |
||
60 | protected $solr; |
||
61 | |||
62 | /** |
||
63 | * @var ConnectionManager |
||
64 | */ |
||
65 | protected $connectionManager; |
||
66 | |||
67 | /** |
||
68 | * Holds options for a specific indexer |
||
69 | * |
||
70 | * @var array |
||
71 | */ |
||
72 | protected $options = []; |
||
73 | |||
74 | /** |
||
75 | * To log or not to log... #Shakespeare |
||
76 | * |
||
77 | * @var bool |
||
78 | */ |
||
79 | protected $loggingEnabled = false; |
||
80 | |||
81 | /** |
||
82 | * @var IdBuilder |
||
83 | */ |
||
84 | protected $variantIdBuilder; |
||
85 | |||
86 | /** |
||
87 | * Cache of the sys_language_overlay information |
||
88 | * |
||
89 | * @var array |
||
90 | */ |
||
91 | protected static $sysLanguageOverlay = []; |
||
92 | |||
93 | /** |
||
94 | * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager |
||
95 | */ |
||
96 | protected $logger = null; |
||
97 | |||
98 | /** |
||
99 | * Constructor |
||
100 | * |
||
101 | * @param array $options array of indexer options |
||
102 | * @param IdBuilder $idBuilder |
||
103 | */ |
||
104 | 16 | public function __construct(array $options = [], IdBuilder $idBuilder = null) |
|
111 | |||
112 | /** |
||
113 | * Indexes an item from the indexing queue. |
||
114 | * |
||
115 | * @param Item $item An index queue item |
||
116 | * @return bool returns true when indexed, false when not |
||
117 | */ |
||
118 | 11 | public function index(Item $item) |
|
145 | |||
146 | /** |
||
147 | * Creates a single Solr Document for an item in a specific language. |
||
148 | * |
||
149 | * @param Item $item An index queue item to index. |
||
150 | * @param int $language The language to use. |
||
151 | * @return bool TRUE if item was indexed successfully, FALSE on failure |
||
152 | */ |
||
153 | 11 | protected function indexItem(Item $item, $language = 0) |
|
154 | { |
||
155 | 11 | $itemIndexed = false; |
|
156 | 11 | $documents = []; |
|
157 | |||
158 | 11 | $itemDocument = $this->itemToDocument($item, $language); |
|
159 | 11 | if (is_null($itemDocument)) { |
|
160 | /* |
||
161 | * If there is no itemDocument, this means there was no translation |
||
162 | * for this record. This should not stop the current item to count as |
||
163 | * being valid because not-indexing not-translated items is perfectly |
||
164 | * fine. |
||
165 | */ |
||
166 | return true; |
||
167 | } |
||
168 | |||
169 | 11 | $documents[] = $itemDocument; |
|
170 | 11 | $documents = array_merge($documents, $this->getAdditionalDocuments($item, $language, $itemDocument)); |
|
171 | 11 | $documents = $this->processDocuments($item, $documents); |
|
172 | 11 | $documents = $this->preAddModifyDocuments($item, $language, $documents); |
|
173 | |||
174 | 11 | $response = $this->solr->addDocuments($documents); |
|
175 | 11 | if ($response->getHttpStatus() == 200) { |
|
176 | 11 | $itemIndexed = true; |
|
177 | 11 | } |
|
178 | |||
179 | 11 | $this->log($item, $documents, $response); |
|
180 | |||
181 | 11 | return $itemIndexed; |
|
182 | } |
||
183 | |||
184 | /** |
||
185 | * Gets the full item record. |
||
186 | * |
||
187 | * This general record indexer simply gets the record from the item. Other |
||
188 | * more specialized indexers may provide more data for their specific item |
||
189 | * types. |
||
190 | * |
||
191 | * @param Item $item The item to be indexed |
||
192 | * @param int $language Language Id (sys_language.uid) |
||
193 | * @return array|NULL The full record with fields of data to be used for indexing or NULL to prevent an item from being indexed |
||
194 | */ |
||
195 | 11 | protected function getFullItemRecord(Item $item, $language = 0) |
|
196 | { |
||
197 | 11 | $rootPageUid = $item->getRootPageUid(); |
|
198 | 11 | $overlayIdentifier = $rootPageUid . '|' . $language; |
|
199 | 11 | if (!isset(self::$sysLanguageOverlay[$overlayIdentifier])) { |
|
200 | 11 | Util::initializeTsfe($rootPageUid, $language); |
|
201 | 11 | self::$sysLanguageOverlay[$overlayIdentifier] = $GLOBALS['TSFE']->sys_language_contentOL; |
|
202 | 11 | } |
|
203 | |||
204 | 11 | $itemRecord = $item->getRecord(); |
|
205 | |||
206 | 11 | if ($language > 0) { |
|
207 | 1 | $page = GeneralUtility::makeInstance(PageRepository::class); |
|
208 | 1 | $page->init(false); |
|
209 | |||
210 | 1 | $itemRecord = $page->getRecordOverlay( |
|
211 | 1 | $item->getType(), |
|
212 | 1 | $itemRecord, |
|
213 | 1 | $language, |
|
214 | 1 | self::$sysLanguageOverlay[$overlayIdentifier] |
|
215 | 1 | ); |
|
216 | 1 | } |
|
217 | |||
218 | 11 | if (!$itemRecord) { |
|
219 | $itemRecord = null; |
||
220 | } |
||
221 | |||
222 | /* |
||
223 | * Skip disabled records. This happens if the default language record |
||
224 | * is hidden but a certain translation isn't. Then the default language |
||
225 | * document appears here but must not be indexed. |
||
226 | */ |
||
227 | 11 | if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled']) |
|
228 | 11 | && $itemRecord[$GLOBALS['TCA'][$item->getType()]['ctrl']['enablecolumns']['disabled']] |
|
229 | 11 | ) { |
|
230 | $itemRecord = null; |
||
231 | } |
||
232 | |||
233 | /* |
||
234 | * Skip translation mismatching records. Sometimes the requested language |
||
235 | * doesn't fit the returned language. This might happen with content fallback |
||
236 | * and is perfectly fine in general. |
||
237 | * But if the requested language doesn't match the returned language and |
||
238 | * the given record has no translation parent, the indexqueue_item most |
||
239 | * probably pointed to a non-translated language record that is dedicated |
||
240 | * to a very specific language. Now we have to avoid indexing this record |
||
241 | * into all language cores. |
||
242 | */ |
||
243 | 11 | $translationOriginalPointerField = 'l10n_parent'; |
|
244 | 11 | if (!empty($GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField'])) { |
|
245 | 10 | $translationOriginalPointerField = $GLOBALS['TCA'][$item->getType()]['ctrl']['transOrigPointerField']; |
|
246 | 10 | } |
|
247 | |||
248 | 11 | $languageField = $GLOBALS['TCA'][$item->getType()]['ctrl']['languageField']; |
|
249 | 11 | if ($itemRecord[$translationOriginalPointerField] == 0 |
|
250 | 11 | && self::$sysLanguageOverlay[$overlayIdentifier] != 1 |
|
251 | 11 | && !empty($languageField) |
|
252 | 11 | && $itemRecord[$languageField] != $language |
|
253 | 11 | && $itemRecord[$languageField] != '-1' |
|
254 | 11 | ) { |
|
255 | $itemRecord = null; |
||
256 | } |
||
257 | |||
258 | 11 | if (!is_null($itemRecord)) { |
|
259 | 11 | $itemRecord['__solr_index_language'] = $language; |
|
260 | 11 | } |
|
261 | |||
262 | 11 | return $itemRecord; |
|
263 | } |
||
264 | |||
265 | /** |
||
266 | * Gets the configuration how to process an item's fields for indexing. |
||
267 | * |
||
268 | * @param Item $item An index queue item |
||
269 | * @param int $language Language ID |
||
270 | * @throws \RuntimeException |
||
271 | * @return array Configuration array from TypoScript |
||
272 | */ |
||
273 | 11 | protected function getItemTypeConfiguration(Item $item, $language = 0) |
|
286 | |||
287 | /** |
||
288 | * Converts an item array (record) to a Solr document by mapping the |
||
289 | * record's fields onto Solr document fields as configured in TypoScript. |
||
290 | * |
||
291 | * @param Item $item An index queue item |
||
292 | * @param int $language Language Id |
||
293 | * @return Apache_Solr_Document The Solr document converted from the record |
||
294 | */ |
||
295 | 11 | protected function itemToDocument(Item $item, $language = 0) |
|
308 | |||
309 | /** |
||
310 | * Creates a Solr document with the basic / core fields set already. |
||
311 | * |
||
312 | * @param Item $item The item to index |
||
313 | * @param array $itemRecord The record to use to build the base document |
||
314 | * @return Apache_Solr_Document A basic Solr document |
||
315 | */ |
||
316 | 11 | protected function getBaseDocument(Item $item, array $itemRecord) |
|
366 | |||
367 | /** |
||
368 | * Generates an Access Rootline for an item. |
||
369 | * |
||
370 | * @param Item $item Index Queue item to index. |
||
371 | * @return string The Access Rootline for the item |
||
372 | */ |
||
373 | 11 | protected function getAccessRootline(Item $item) |
|
391 | |||
392 | /** |
||
393 | * Sends the documents to the field processing service which takes care of |
||
394 | * manipulating fields as defined in the field's configuration. |
||
395 | * |
||
396 | * @param Item $item An index queue item |
||
397 | * @param array $documents An array of Apache_Solr_Document objects to manipulate. |
||
398 | * @return array Array of manipulated Apache_Solr_Document objects. |
||
399 | */ |
||
400 | 11 | protected function processDocuments(Item $item, array $documents) |
|
417 | |||
418 | /** |
||
419 | * Allows third party extensions to provide additional documents which |
||
420 | * should be indexed for the current item. |
||
421 | * |
||
422 | * @param Item $item The item currently being indexed. |
||
423 | * @param int $language The language uid currently being indexed. |
||
424 | * @param Apache_Solr_Document $itemDocument The document representing the item for the given language. |
||
425 | * @return array An array of additional Apache_Solr_Document objects to index. |
||
426 | */ |
||
427 | 15 | protected function getAdditionalDocuments( |
|
428 | Item $item, |
||
429 | $language, |
||
430 | Apache_Solr_Document $itemDocument |
||
431 | ) { |
||
432 | 15 | $documents = []; |
|
433 | |||
434 | 15 | if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'])) { |
|
435 | 4 | foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['IndexQueueIndexer']['indexItemAddDocuments'] as $classReference) { |
|
436 | 4 | if (!class_exists($classReference)) { |
|
437 | 2 | throw new \InvalidArgumentException('Class does not exits' . $classReference, 1490363487); |
|
438 | } |
||
439 | 2 | $additionalIndexer = GeneralUtility::makeInstance($classReference); |
|
440 | 2 | if ($additionalIndexer instanceof AdditionalIndexQueueItemIndexer) { |
|
441 | 1 | $additionalDocuments = $additionalIndexer->getAdditionalItemDocuments($item, |
|
442 | 1 | $language, $itemDocument); |
|
443 | |||
444 | 1 | if (is_array($additionalDocuments)) { |
|
445 | 1 | $documents = array_merge($documents, |
|
446 | 1 | $additionalDocuments); |
|
447 | 1 | } |
|
448 | 1 | } else { |
|
449 | 1 | throw new \UnexpectedValueException( |
|
450 | 1 | get_class($additionalIndexer) . ' must implement interface ' . AdditionalIndexQueueItemIndexer::class, |
|
451 | 1326284551 |
||
452 | 1 | ); |
|
453 | } |
||
454 | 1 | } |
|
455 | 1 | } |
|
456 | 12 | return $documents; |
|
457 | } |
||
458 | |||
459 | /** |
||
460 | * Provides a hook to manipulate documents right before they get added to |
||
461 | * the Solr index. |
||
462 | * |
||
463 | * @param Item $item The item currently being indexed. |
||
464 | * @param int $language The language uid of the documents |
||
465 | * @param array $documents An array of documents to be indexed |
||
466 | * @return array An array of modified documents |
||
467 | */ |
||
468 | 11 | protected function preAddModifyDocuments( |
|
494 | |||
495 | // Initialization |
||
496 | |||
497 | /** |
||
498 | * Gets the Solr connections applicable for an item. |
||
499 | * |
||
500 | * The connections include the default connection and connections to be used |
||
501 | * for translations of an item. |
||
502 | * |
||
503 | * @param Item $item An index queue item |
||
504 | * @return array An array of ApacheSolrForTypo3\Solr\SolrService connections, the array's keys are the sys_language_uid of the language of the connection |
||
505 | */ |
||
506 | 12 | protected function getSolrConnectionsByItem(Item $item) |
|
507 | { |
||
508 | 12 | $solrConnections = []; |
|
509 | |||
510 | 12 | $pageId = $item->getRootPageUid(); |
|
511 | 12 | if ($item->getType() == 'pages') { |
|
512 | 2 | $pageId = $item->getRecordUid(); |
|
513 | 2 | } |
|
514 | |||
515 | // Solr configurations possible for this item |
||
516 | 12 | $site = $item->getSite(); |
|
517 | 12 | $solrConfigurationsBySite = $this->connectionManager->getConfigurationsBySite($site); |
|
518 | |||
519 | 12 | $siteLanguages = []; |
|
520 | 12 | foreach ($solrConfigurationsBySite as $solrConfiguration) { |
|
521 | 12 | $siteLanguages[] = $solrConfiguration['language']; |
|
522 | 12 | } |
|
523 | |||
524 | 12 | $translationOverlays = $this->getTranslationOverlaysForPage($pageId, $site->getSysLanguageMode()); |
|
525 | 12 | foreach ($translationOverlays as $key => $translationOverlay) { |
|
526 | 1 | if (!in_array($translationOverlay['sys_language_uid'], |
|
527 | 1 | $siteLanguages) |
|
528 | 1 | ) { |
|
529 | unset($translationOverlays[$key]); |
||
530 | } |
||
531 | 12 | } |
|
532 | |||
533 | 12 | $defaultConnection = $this->connectionManager->getConnectionByPageId($pageId); |
|
534 | 12 | $translationConnections = $this->getConnectionsForIndexableLanguages($translationOverlays); |
|
535 | |||
536 | 12 | $solrConnections[0] = $defaultConnection; |
|
537 | 12 | foreach ($translationConnections as $systemLanguageUid => $solrConnection) { |
|
538 | 1 | $solrConnections[$systemLanguageUid] = $solrConnection; |
|
539 | 12 | } |
|
540 | |||
541 | 12 | return $solrConnections; |
|
542 | } |
||
543 | |||
544 | /** |
||
545 | * Finds the alternative page language overlay records for a page based on |
||
546 | * the sys_language_mode. |
||
547 | * |
||
548 | * Possible Language Modes: |
||
549 | * 1) content_fallback --> all languages |
||
550 | * 2) strict --> available languages with page overlay |
||
551 | * 3) ignore --> available languages with page overlay |
||
552 | * 4) unknown mode or blank --> all languages |
||
553 | * |
||
554 | * @param int $pageId Page ID. |
||
555 | * @param string $languageMode |
||
556 | * @return array An array of translation overlays (or fake overlays) found for the given page. |
||
557 | */ |
||
558 | 12 | protected function getTranslationOverlaysForPage($pageId, $languageMode) |
|
559 | { |
||
560 | 12 | $translationOverlays = []; |
|
561 | 12 | $pageId = intval($pageId); |
|
562 | |||
563 | 12 | $languageModes = ['content_fallback', 'strict', 'ignore']; |
|
564 | 12 | $hasOverlayMode = in_array($languageMode, $languageModes, |
|
565 | 12 | true); |
|
566 | 12 | $isContentFallbackMode = ($languageMode === 'content_fallback'); |
|
567 | |||
568 | 12 | if ($hasOverlayMode && !$isContentFallbackMode) { |
|
569 | 1 | $translationOverlays = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( |
|
570 | 1 | 'pid, sys_language_uid', |
|
571 | 1 | 'pages_language_overlay', |
|
572 | 'pid = ' . $pageId |
||
573 | 1 | . BackendUtility::deleteClause('pages_language_overlay') |
|
574 | 1 | . BackendUtility::BEenableFields('pages_language_overlay') |
|
575 | 1 | ); |
|
576 | 1 | } else { |
|
577 | // ! If no sys_language_mode is configured, all languages will be indexed ! |
||
578 | 11 | $languages = $this->getSystemLanguages(); |
|
579 | |||
580 | 11 | foreach ($languages as $language) { |
|
581 | 11 | if ($language['uid'] <= 0) { |
|
582 | 11 | continue; |
|
583 | } |
||
584 | $translationOverlays[] = [ |
||
585 | 'pid' => $pageId, |
||
586 | 'sys_language_uid' => $language['uid'], |
||
587 | ]; |
||
588 | 11 | } |
|
589 | } |
||
590 | |||
591 | 12 | return $translationOverlays; |
|
592 | } |
||
593 | |||
594 | /** |
||
595 | * Returns an array of system languages. |
||
596 | * |
||
597 | * @return array |
||
598 | */ |
||
599 | 11 | protected function getSystemLanguages() |
|
603 | |||
604 | /** |
||
605 | * Checks for which languages connections have been configured and returns |
||
606 | * these connections. |
||
607 | * |
||
608 | * @param array $translationOverlays An array of translation overlays to check for configured connections. |
||
609 | * @return array An array of ApacheSolrForTypo3\Solr\SolrService connections. |
||
610 | */ |
||
611 | 12 | protected function getConnectionsForIndexableLanguages( |
|
612 | array $translationOverlays |
||
613 | ) { |
||
614 | 12 | $connections = []; |
|
615 | |||
616 | 12 | foreach ($translationOverlays as $translationOverlay) { |
|
617 | 1 | $pageId = $translationOverlay['pid']; |
|
618 | 1 | $languageId = $translationOverlay['sys_language_uid']; |
|
619 | |||
620 | try { |
||
621 | 1 | $connection = $this->connectionManager->getConnectionByPageId($pageId, |
|
622 | 1 | $languageId); |
|
623 | 1 | $connections[$languageId] = $connection; |
|
624 | 1 | } catch (NoSolrConnectionFoundException $e) { |
|
625 | // ignore the exception as we seek only those connections |
||
626 | // actually available |
||
627 | } |
||
628 | 12 | } |
|
629 | |||
630 | 12 | return $connections; |
|
631 | } |
||
632 | |||
633 | // Utility methods |
||
634 | |||
635 | // FIXME extract log() and setLogging() to ApacheSolrForTypo3\Solr\IndexQueue\AbstractIndexer |
||
636 | // FIXME extract an interface Tx_Solr_IndexQueue_ItemInterface |
||
637 | |||
638 | /** |
||
639 | * Enables logging dependent on the configuration of the item's site |
||
640 | * |
||
641 | * @param Item $item An item being indexed |
||
642 | * @return void |
||
643 | */ |
||
644 | 12 | protected function setLogging(Item $item) |
|
645 | { |
||
646 | 12 | $solrConfiguration = Util::getSolrConfigurationFromPageId($item->getRootPageUid()); |
|
647 | 12 | $this->loggingEnabled = $solrConfiguration->getLoggingIndexingQueueOperationsByConfigurationNameWithFallBack( |
|
648 | 12 | $item->getIndexingConfigurationName() |
|
649 | 12 | ); |
|
650 | 12 | } |
|
651 | |||
652 | /** |
||
653 | * Logs the item and what document was created from it |
||
654 | * |
||
655 | * @param Item $item The item that is being indexed. |
||
656 | * @param array $itemDocuments An array of Solr documents created from the item's data |
||
657 | * @param Apache_Solr_Response $response The Solr response for the particular index document |
||
658 | */ |
||
659 | 11 | protected function log( |
|
700 | } |
||
701 |