Passed
Push — release-11.5.x ( 85d260...6561e3 )
by Markus
38:59
created

DataUpdateHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
dl 0
loc 20
ccs 8
cts 8
cp 1
rs 10
c 1
b 0
f 0
cc 1
nc 1
nop 9
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types = 1);
2
namespace ApacheSolrForTypo3\Solr\Domain\Index\Queue\UpdateHandler;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2021 Markus Friedrich <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use TYPO3\CMS\Core\DataHandling\DataHandler;
28
use TYPO3\CMS\Backend\Utility\BackendUtility;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use ApacheSolrForTypo3\Solr\FrontendEnvironment;
31
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
32
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\RootPageResolver;
33
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService;
34
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\MountPagesUpdater;
35
use ApacheSolrForTypo3\Solr\Domain\Site\SiteInterface;
36
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
37
use ApacheSolrForTypo3\Solr\System\TCA\TCAService;
38
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
39
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
40
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
41
use ApacheSolrForTypo3\Solr\Util;
42
43
44
/**
45
 * Data update handler
46
 *
47
 * Handles update on potential relevant records e.g.
48
 * an update might require index queue updates
49
 */
50
class DataUpdateHandler extends AbstractUpdateHandler
51
{
52
    /**
53
     * List of fields in the update field array that
54
     * are required for processing
55
     *
56
     * Note: For pages all fields except l10n_diffsource are
57
     *       kept, as additional fields can be configured in
58
     *       TypoScript, see AbstractDataUpdateEvent->_sleep.
59
     *
60
     * @var array
61
     */
62
    protected static $requiredUpdatedFields = [
63
        'pid',
64
    ];
65
66
    /**
67
     * Configuration used to check if recursive updates are required
68
     *
69
     * Holds the configuration when a recursive page queuing should be triggered, while processing record
70
     * updates
71
     *
72
     * Note: The SQL transaction is already committed, so the current state covers only "non"-changed fields.
73
     *
74
     * @var array
75
     */
76
    protected $updateSubPagesRecursiveTriggerConfiguration = [
77
        // the current page has the both fields "extendToSubpages" and "hidden" set from 1 to 0 => requeue subpages
78
        'HiddenAndExtendToSubpageWereDisabled' => [
79
            'changeSet' => [
80
                'hidden' => '0',
81
                'extendToSubpages' => '0'
82
            ]
83
        ],
84
        // the current page has the field "extendToSubpages" enabled and the field "hidden" was set to 0 => requeue subpages
85
        'extendToSubpageEnabledAndHiddenFlagWasRemoved' => [
86
            'currentState' =>  ['extendToSubpages' => '1'],
87
            'changeSet' => ['hidden' => '0']
88
        ],
89
        // the current page has the field "hidden" enabled and the field "extendToSubpages" was set to 0 => requeue subpages
90
        'hiddenIsEnabledAndExtendToSubPagesWasRemoved' => [
91
            'currentState' =>  ['hidden' => '1'],
92
            'changeSet' => ['extendToSubpages' => '0']
93
        ],
94
        // the field "no_search_sub_entries" of current page was set to 0
95
        'no_search_sub_entriesFlagWasAdded' => [
96
            'changeSet' => ['no_search_sub_entries' => '0']
97
        ],
98
    ];
99
100
    /**
101
     * @var MountPagesUpdater
102
     */
103
    protected $mountPageUpdater;
104
105
    /**
106
     * @var RootPageResolver
107
     */
108
    protected $rootPageResolver = null;
109
110
    /**
111
     * @var PagesRepository
112
     */
113
    protected $pagesRepository;
114
115
    /**
116
     * @var SolrLogManager
117
     */
118
    protected $logger = null;
119
120
    /**
121
     * @var DataHandler
122
     */
123
    protected $dataHandler;
124
125
    /**
126
     * @param ConfigurationAwareRecordService $recordService
127
     * @param FrontendEnvironment $frontendEnvironment
128
     * @param TCAService $tcaService
129
     * @param Queue $indexQueue
130
     * @param MountPagesUpdater $mountPageUpdater
131
     * @param RootPageResolver $rootPageResolver
132
     * @param PagesRepository $pagesRepository
133
     * @param SolrLogManager $solrLogManager
134
     * @param DataHandler $dataHandler
135
     */
136 82
    public function __construct(
137
        ConfigurationAwareRecordService $recordService ,
138
        FrontendEnvironment $frontendEnvironment,
139
        TCAService $tcaService,
140
        Queue $indexQueue,
141
        MountPagesUpdater $mountPageUpdater,
142
        RootPageResolver $rootPageResolver,
143
        PagesRepository $pagesRepository,
144
        DataHandler $dataHandler,
145
        SolrLogManager $solrLogManager = null
146
    ) {
147 82
        parent::__construct($recordService, $frontendEnvironment, $tcaService, $indexQueue);
148
149 82
        $this->mountPageUpdater = $mountPageUpdater;
150 82
        $this->rootPageResolver = $rootPageResolver;
151 82
        $this->pagesRepository = $pagesRepository;
152 82
        $this->dataHandler = $dataHandler;
153 82
        $this->logger = $solrLogManager ?? GeneralUtility::makeInstance(
154 65
            SolrLogManager::class,
155
            /** @scrutinizer ignore-type */ __CLASS__
156 65
        );
157 82
    }
158
159
    /**
160
     * Handle content element update
161
     *
162
     * @param int $uid
163
     * @param array $updatedFields
164
     */
165 15
    public function handleContentElementUpdate(int $uid, array $updatedFields = []): void
166
    {
167 15
        $pid = $updatedFields['pid'] ?? $this->getValidatedPid('tt_content', $uid);
168 15
        if ($pid === null) {
169 1
            return;
170
        }
171
172 14
        $this->processPageRecord($pid, (int)$pid, $updatedFields);
173 14
    }
174
175
    /**
176
     * Handles the deletion of a content element
177
     *
178
     * @param int $uid
179
     */
180 3
    public function handleContentElementDeletion(int $uid): void
181
    {
182
        // @TODO: Should be checked, is possibly unnecessary as
183
        //        also done via GarbageCollector & PageStrategy
184
185 3
        $pid = $this->getValidatedPid('tt_content', $uid);
186 3
        if ($pid === null) {
187
            return;
188
        }
189
190 3
        $this->indexQueue->updateItem('pages', $pid, Util::getExectionTime());
191 3
    }
192
193
    /**
194
     * Handles page updates
195
     *
196
     * @param int $uid
197
     * @param array $updatedFields
198
     */
199 43
    public function handlePageUpdate(int $uid, array $updatedFields = []): void
200
    {
201
        try {
202 43
            if (isset($updatedFields['l10n_parent']) && intval($updatedFields['l10n_parent']) > 0) {
203 2
                $pid = $updatedFields['l10n_parent'];
204 41
            } elseif ($this->rootPageResolver->getIsRootPageId($uid)) {
205 9
                $pid = $uid;
206
            } else {
207 41
                $pid = $updatedFields['pid'] ?? $this->getValidatedPid('pages', $uid);
208
            }
209 2
        } catch (\Throwable $e) {
210 2
            $pid = null;
211
        }
212
213 43
        if ($pid === null) {
214 2
            $this->removeFromIndexAndQueueWhenItemInQueue('pages', $uid);
215 2
            return;
216
        }
217
218 41
        $this->processPageRecord($uid, (int)$pid, $updatedFields);
219 41
    }
220
221
    /**
222
     * Handles record updates
223
     *
224
     * @param int $uid
225
     * @param string $table
226
     */
227 13
    public function handleRecordUpdate(int $uid, string $table): void
228
    {
229 13
        $rootPageIds = $this->getRecordRootPageIds($table, $uid);
230 13
        $this->processRecord($table, $uid, $rootPageIds);
231 13
    }
232
233
    /**
234
     * Handles a version swap
235
     *
236
     * @param int $uid
237
     * @param string $table
238
     */
239 6
    public function handleVersionSwap(int $uid, string $table): void
240
    {
241 6
        $isPageRelatedRecord = ($table === 'tt_content' || $table === 'pages');
242 6
        if($isPageRelatedRecord) {
243 4
            $uid = ($table === 'tt_content' ? $this->getValidatedPid($table, $uid) : $uid);
244 4
            if ($uid === null) {
245
                return;
246
            }
247 4
            $this->applyPageChangesToQueue($uid);
248
        } else {
249 2
            $recordPageId = $this->getValidatedPid($table, $uid);
250 2
            if ($recordPageId === null) {
251
                return;
252
            }
253 2
            $this->applyRecordChangesToQueue($table, $uid, $recordPageId);
254
        }
255 6
    }
256
257
    /**
258
     * Handle page move
259
     *
260
     * @param int $uid
261
     */
262 1
    public function handleMovedPage(int $uid): void
263
    {
264 1
        $this->applyPageChangesToQueue($uid);
265 1
    }
266
267
    /**
268
     * Handle record move
269
     *
270
     * @param int $uid
271
     * @param string $table
272
     */
273 1
    public function handleMovedRecord(int $uid, string $table): void
274
    {
275 1
        $pid = $this->getValidatedPid($table, $uid);
276 1
        if ($pid === null) {
277
            return;
278
        }
279
280 1
        $this->applyRecordChangesToQueue($table, $uid, $pid);
281 1
    }
282
283
    /**
284
     * Adds a page to the queue and updates mounts, when it is enabled, otherwise ensure that the page is removed
285
     * from the queue.
286
     *
287
     * @param int $uid
288
     */
289 5
    protected function applyPageChangesToQueue(int $uid): void
290
    {
291 5
        $solrConfiguration = $this->getSolrConfigurationFromPageId($uid);
292 5
        $record = $this->configurationAwareRecordService->getRecord('pages', $uid, $solrConfiguration);
293 5
        if (!empty($record) && $this->tcaService->isEnabledRecord('pages', $record)) {
294 4
            $this->mountPageUpdater->update($uid);
295 4
            $this->indexQueue->updateItem('pages', $uid);
296
        } else {
297 1
            $this->removeFromIndexAndQueueWhenItemInQueue('pages', $uid);
298
        }
299 5
    }
300
301
    /**
302
     * Adds a record to the queue if it is monitored and enabled, otherwise it removes the record from the queue.
303
     *
304
     * @param string $table
305
     * @param int $uid
306
     * @param int $pid
307
     */
308 3
    protected function applyRecordChangesToQueue(string $table, int $uid, int $pid): void
309
    {
310 3
        $solrConfiguration = $this->getSolrConfigurationFromPageId($pid);
311 3
        $isMonitoredTable = $solrConfiguration->getIndexQueueIsMonitoredTable($table);
312
313 3
        if ($isMonitoredTable) {
314 3
            $record = $this->configurationAwareRecordService->getRecord($table, $uid, $solrConfiguration);
315
316 3
            if (!empty($record) && $this->tcaService->isEnabledRecord($table, $record)) {
317 2
                $uid = $this->tcaService->getTranslationOriginalUidIfTranslated($table, $record, $uid);
318 2
                $this->indexQueue->updateItem($table, $uid);
319
            } else {
320
                // TODO should be moved to garbage collector
321 1
                $this->removeFromIndexAndQueueWhenItemInQueue($table, $uid);
322
            }
323
        }
324 3
    }
325
326
    /**
327
     * Removes record from the index queue and from the solr index
328
     *
329
     * @param string $recordTable Name of table where the record lives
330
     * @param int $recordUid Id of record
331
     */
332 6
    protected function removeFromIndexAndQueue(string $recordTable, int $recordUid): void
333
    {
334 6
        $this->getGarbageHandler()->collectGarbage($recordTable, $recordUid);
335 6
    }
336
337
    /**
338
     * Removes record from the index queue and from the solr index when the item is in the queue.
339
     *
340
     * @param string $recordTable Name of table where the record lives
341
     * @param int $recordUid Id of record
342
     */
343 8
    protected function removeFromIndexAndQueueWhenItemInQueue(string $recordTable, int $recordUid): void
344
    {
345 8
        if (!$this->indexQueue->containsItem($recordTable, $recordUid)) {
346 2
            return;
347
        }
348
349 6
        $this->removeFromIndexAndQueue($recordTable, $recordUid);
350 6
    }
351
352
    /**
353
     * @param $pageId
354
     * @return TypoScriptConfiguration
355
     */
356 8
    protected function getSolrConfigurationFromPageId(int $pageId): TypoScriptConfiguration
357
    {
358 8
        return $this->frontendEnvironment->getSolrConfigurationFromPageId($pageId);
359
    }
360
361
    /**
362
     * Fetch record root page ids
363
     *
364
     * @param string $recordTable The table the record belongs to
365
     * @param int $recordUid
366
     * @return int[]
367
     */
368 13
    protected function getRecordRootPageIds(string $recordTable, int $recordUid): array
369
    {
370
        try {
371 13
            $rootPageIds = $this->rootPageResolver->getResponsibleRootPageIds($recordTable, $recordUid);
372
        } catch (\InvalidArgumentException $e) {
373
            $rootPageIds = [];
374
        }
375
376 13
        return $rootPageIds;
377
    }
378
379
    /**
380
     * Processes a page record
381
     *
382
     * Note: Also used if content element is updated, the page
383
     * of the content element is processed here
384
     *
385
     * @param int $uid
386
     * @param int $pid
387
     * @param array $updatedFields
388
     */
389 55
    protected function processPageRecord(int $uid, int $pid, array $updatedFields = []): void
390
    {
391 55
        $configurationPageId = $this->getConfigurationPageId('pages', (int)$pid, $uid);
392 55
        if ($configurationPageId === 0) {
393 2
            $this->mountPageUpdater->update($uid);
394 2
            return;
395
        }
396 53
        $rootPageIds = [$configurationPageId];
397
398 53
        $this->processRecord('pages', $uid, $rootPageIds);
399
400 53
        $this->updateCanonicalPages($uid);
401 53
        $this->mountPageUpdater->update($uid);
402
403 53
        $recursiveUpdateRequired = $this->isRecursivePageUpdateRequired($uid, $updatedFields);
404 53
        if ($recursiveUpdateRequired) {
405 12
            $treePageIds = $this->getSubPageIds($uid);
406 12
            $this->updatePageIdItems($treePageIds);
407
        }
408 53
    }
409
410
    /**
411
     * Process a record
412
     *
413
     * @param string $recordTable
414
     * @param int $recordUid
415
     * @param array $rootPageIds
416
     */
417 66
    protected function processRecord(string $recordTable, int $recordUid, array $rootPageIds): void
418
    {
419 66
        if (empty($rootPageIds)) {
420
            $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
421
            return;
422
        }
423
424 66
        foreach ($rootPageIds as $configurationPageId) {
425 66
            $site = $this->getSiteRepository()->getSiteByPageId($configurationPageId);
426 66
            if (!$site instanceof SiteInterface) {
427
                continue;
428
            }
429 66
            $solrConfiguration = $site->getSolrConfiguration();
430 66
            $isMonitoredRecord = $solrConfiguration->getIndexQueueIsMonitoredTable($recordTable);
431 66
            if (!$isMonitoredRecord) {
432
                // when it is a non monitored record, we can skip it.
433 1
                continue;
434
            }
435
436 65
            $record = $this->configurationAwareRecordService->getRecord($recordTable, $recordUid, $solrConfiguration);
437 65
            if (empty($record)) {
438
                // TODO move this part to the garbage collector
439
                // check if the item should be removed from the index because it no longer matches the conditions
440 4
                $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
441 4
                continue;
442
            }
443
            // Clear existing index queue items to prevent mount point duplicates.
444
            // This needs to be done before the overlay handling, because handling an overlay record should
445
            // not trigger a deletion.
446 63
            $isTranslation = !empty($record['sys_language_uid']) && $record['sys_language_uid'] !== 0;
447 63
            if ($recordTable === 'pages' && !$isTranslation) {
448 48
                $this->indexQueue->deleteItem('pages', $recordUid);
449
            }
450
451
            // The pages localized record can not consist without l10n_parent, so apply "free-content-mode" on records only.
452 63
            if ($recordTable === 'pages' || !$site->hasFreeContentModeLanguages() || !in_array($record['sys_language_uid'], $site->getFreeContentModeLanguages())) {
0 ignored issues
show
Bug introduced by
The method getFreeContentModeLanguages() does not exist on ApacheSolrForTypo3\Solr\Domain\Site\SiteInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ApacheSolrForTypo3\Solr\Domain\Site\SiteInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

452
            if ($recordTable === 'pages' || !$site->hasFreeContentModeLanguages() || !in_array($record['sys_language_uid'], $site->/** @scrutinizer ignore-call */ getFreeContentModeLanguages())) {
Loading history...
Bug introduced by
The method hasFreeContentModeLanguages() does not exist on ApacheSolrForTypo3\Solr\Domain\Site\SiteInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to ApacheSolrForTypo3\Solr\Domain\Site\SiteInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

452
            if ($recordTable === 'pages' || !$site->/** @scrutinizer ignore-call */ hasFreeContentModeLanguages() || !in_array($record['sys_language_uid'], $site->getFreeContentModeLanguages())) {
Loading history...
453 63
                $recordUid = $this->tcaService->getTranslationOriginalUidIfTranslated($recordTable, $record, $recordUid);
454
            }
455
456
            // only update/insert the item if we actually found a record
457 63
            $isLocalizedRecord = $this->tcaService->isLocalizedRecord($recordTable, $record);
458
459 63
            if ($isLocalizedRecord && !$this->getIsTranslationParentRecordEnabled($recordTable, $recordUid)) {
460
                // we have a localized record without a visible parent record. Nothing to do.
461
                continue;
462
            }
463
464 63
            if ($this->tcaService->isEnabledRecord($recordTable, $record)) {
465 57
                $this->indexQueue->updateItem($recordTable, $recordUid);
466
            }
467
        }
468 66
    }
469
470
    /**
471
     * This method is used to determine the pageId that should be used to retrieve the index queue configuration.
472
     *
473
     * @param string $recordTable
474
     * @param int $recordPageId
475
     * @param int $recordUid
476
     * @return int
477
     */
478 55
    protected function getConfigurationPageId(string $recordTable, int $recordPageId, int $recordUid): int
479
    {
480 55
        $rootPageId = $this->rootPageResolver->getRootPageId($recordPageId);
481 55
        $rootPageRecord = $this->getPagesRepository()->getPage((int)$rootPageId);
482 55
        if (isset($rootPageRecord['sys_language_uid'])
483 55
            && (int)$rootPageRecord['sys_language_uid'] > 0
484 55
            && isset($rootPageRecord['l10n_parent'])
485 55
            && (int)$rootPageRecord['l10n_parent'] > 0
486
        ) {
487 2
            $rootPageId = $recordPageId = $rootPageRecord['l10n_parent'];
488
        }
489 55
        if ($this->rootPageResolver->getIsRootPageId($rootPageId)) {
490 53
            return $recordPageId;
491
        }
492
493 2
        $alternativeSiteRoots = $this->rootPageResolver->getAlternativeSiteRootPagesIds(
494 2
            $recordTable,
495
            $recordUid,
496
            $recordPageId
497
        );
498 2
        return (int)array_pop($alternativeSiteRoots);
499
    }
500
501
    /**
502
     * Checks if the parent record of the translated record is enabled.
503
     *
504
     * @param string $recordTable
505
     * @param int $recordUid
506
     * @return bool
507
     */
508 3
    protected function getIsTranslationParentRecordEnabled(string $recordTable, int $recordUid): bool
509
    {
510 3
        $l10nParentRecord = (array)BackendUtility::getRecord($recordTable, $recordUid, '*', '', false);
511 3
        return $this->tcaService->isEnabledRecord($recordTable, $l10nParentRecord);
512
    }
513
514
    /**
515
     * Applies the updateItem instruction on a collection of pageIds.
516
     *
517
     * @param array $treePageIds
518
     */
519 12
    protected function updatePageIdItems(array $treePageIds): void
520
    {
521 12
        foreach ($treePageIds as $treePageId) {
522 10
            $this->indexQueue->updateItem('pages', $treePageId);
523
        }
524 12
    }
525
526
    /**
527
     * Triggers Index Queue updates for other pages showing content from the
528
     * page currently being updated.
529
     *
530
     * @param int $pageId UID of the page currently being updated
531
     */
532 53
    protected function updateCanonicalPages(int $pageId): void
533
    {
534 53
        $canonicalPages = $this->pagesRepository->findPageUidsWithContentsFromPid((int)$pageId);
535 53
        foreach ($canonicalPages as $page) {
536
            $this->indexQueue->updateItem('pages', $page['uid']);
537
        }
538 53
    }
539
540
    /**
541
     * Retrieves the pid of a record, returns null if no pid could be found
542
     *
543
     * @param string $table
544
     * @param int $uid
545
     * @return int|null
546
     */
547 45
    protected function getValidatedPid(string $table, int $uid): ?int
548
    {
549 45
        $pid = $this->dataHandler->getPID($table, $uid);
550 45
        if ($pid === false) {
551 1
            $message = 'Record without valid pid was processed ' . $table . ':' . $uid;
552 1
            $this->logger->log(SolrLogManager::WARNING, $message);
553 1
            return null;
554
        }
555
556 44
        return (int)$pid;
557
    }
558
559
    /**
560
     * @return GarbageHandler
561
     */
562 6
    protected function getGarbageHandler(): GarbageHandler
563
    {
564 6
        return GeneralUtility::makeInstance(GarbageHandler::class);
565
    }
566
567
    /**
568
     * @return SiteRepository
569
     */
570 66
    protected function getSiteRepository(): SiteRepository
571
    {
572 66
        return GeneralUtility::makeInstance(SiteRepository::class);
573
    }
574
}
575