Failed Conditions
Push — release-11.2.x ( 3aa391...494b52 )
by Rafael
18:23
created

DataUpdateHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
dl 0
loc 20
ccs 9
cts 9
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\System\TCA\TCAService;
36
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
37
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
38
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
39
use ApacheSolrForTypo3\Solr\Util;
40
41
/**
42
 * Data update handler
43
 *
44
 * Handles update on potential relevant records e.g.
45
 * an update might require index queue updates
46
 */
47
class DataUpdateHandler extends AbstractUpdateHandler
48
{
49
    /**
50
     * List of fields in the update field array that
51
     * are required for processing
52
     *
53
     * Note: For pages all fields except l10n_diffsource are
54
     *       kept, as additional fields can be configured in
55
     *       TypoScript, see AbstractDataUpdateEvent->_sleep.
56
     *
57
     * @var array
58
     */
59
    protected static $requiredUpdatedFields = [
60
        'pid',
61
    ];
62
63
    /**
64
     * Configuration used to check if recursive updates are required
65
     *
66
     * Holds the configuration when a recursive page queuing should be triggered, while processing record
67
     * updates
68
     *
69
     * Note: The SQL transaction is already committed, so the current state covers only "non"-changed fields.
70
     *
71
     * @var array
72
     */
73
    protected $updateSubPagesRecursiveTriggerConfiguration = [
74
        // the current page has the both fields "extendToSubpages" and "hidden" set from 1 to 0 => requeue subpages
75
        'HiddenAndExtendToSubpageWereDisabled' => [
76
            'changeSet' => [
77
                'hidden' => '0',
78
                'extendToSubpages' => '0'
79
            ]
80
        ],
81
        // the current page has the field "extendToSubpages" enabled and the field "hidden" was set to 0 => requeue subpages
82
        'extendToSubpageEnabledAndHiddenFlagWasRemoved' => [
83
            'currentState' =>  ['extendToSubpages' => '1'],
84
            'changeSet' => ['hidden' => '0']
85
        ],
86
        // the current page has the field "hidden" enabled and the field "extendToSubpages" was set to 0 => requeue subpages
87
        'hiddenIsEnabledAndExtendToSubPagesWasRemoved' => [
88
            'currentState' =>  ['hidden' => '1'],
89
            'changeSet' => ['extendToSubpages' => '0']
90
        ],
91
        // the field "no_search_sub_entries" of current page was set to 0
92
        'no_search_sub_entriesFlagWasAdded' => [
93
            'changeSet' => ['no_search_sub_entries' => '0']
94
        ],
95
    ];
96
97
    /**
98
     * @var MountPagesUpdater
99
     */
100
    protected $mountPageUpdater;
101
102
    /**
103
     * @var RootPageResolver
104
     */
105
    protected $rootPageResolver = null;
106
107
    /**
108
     * @var PagesRepository
109
     */
110
    protected $pagesRepository;
111
112
    /**
113
     * @var SolrLogManager
114
     */
115
    protected $logger = null;
116
117
    /**
118
     * @var DataHandler
119
     */
120
    protected $dataHandler;
121
122
    /**
123
     * @param ConfigurationAwareRecordService $recordService
124
     * @param FrontendEnvironment $frontendEnvironment
125
     * @param TCAService $tcaService
126
     * @param Queue $indexQueue
127
     * @param MountPagesUpdater $mountPageUpdater
128
     * @param RootPageResolver $rootPageResolver
129
     * @param PagesRepository $pagesRepository
130
     * @param SolrLogManager $solrLogManager
131
     * @param DataHandler $dataHandler
132
     */
133 81
    public function __construct(
134
        ConfigurationAwareRecordService $recordService ,
135
        FrontendEnvironment $frontendEnvironment,
136
        TCAService $tcaService,
137
        Queue $indexQueue,
138
        MountPagesUpdater $mountPageUpdater,
139
        RootPageResolver $rootPageResolver,
140
        PagesRepository $pagesRepository,
141
        DataHandler $dataHandler,
142
        SolrLogManager $solrLogManager = null
143
    ) {
144 81
        parent::__construct($recordService, $frontendEnvironment, $tcaService, $indexQueue);
145
146 81
        $this->mountPageUpdater = $mountPageUpdater;
147 81
        $this->rootPageResolver = $rootPageResolver;
148 81
        $this->pagesRepository = $pagesRepository;
149 81
        $this->dataHandler = $dataHandler;
150 81
        $this->logger = $solrLogManager ?? GeneralUtility::makeInstance(
151 64
            SolrLogManager::class,
152 64
            /** @scrutinizer ignore-type */ __CLASS__
153
        );
154 81
    }
155
156
    /**
157
     * Handle content element update
158
     *
159
     * @param int $uid
160
     * @param array $updatedFields
161
     */
162 15
    public function handleContentElementUpdate(int $uid, array $updatedFields = []): void
163
    {
164 15
        $pid = $updatedFields['pid'] ?? $this->getValidatedPid('tt_content', $uid);
165 15
        if ($pid === null) {
166 1
            return;
167
        }
168
169 14
        $this->processPageRecord($pid, (int)$pid, $updatedFields);
170 14
    }
171
172
    /**
173
     * Handles the deletion of a content element
174
     *
175
     * @param int $uid
176
     */
177 2
    public function handleContentElementDeletion(int $uid): void
178
    {
179
        // @TODO: Should be checked, is possibly unnecessary as
180
        //        also done via GarbageCollector & PageStrategy
181
182 2
        $pid = $this->getValidatedPid('tt_content', $uid);
183 2
        if ($pid === null) {
184
            return;
185
        }
186
187 2
        $this->indexQueue->updateItem('pages', $pid, Util::getExectionTime());
188 2
    }
189
190
    /**
191
     * Handles page updates
192
     *
193
     * @param int $uid
194
     * @param array $updatedFields
195
     */
196 43
    public function handlePageUpdate(int $uid, array $updatedFields = []): void
197
    {
198 43
        if ($uid === 0) {
199 1
            return;
200
        }
201
        try {
202 42
            if (isset($updatedFields['l10n_parent']) && intval($updatedFields['l10n_parent']) > 0) {
203 2
                $pid = $updatedFields['l10n_parent'];
204 40
            } elseif ($this->rootPageResolver->getIsRootPageId($uid)) {
205 18
                $pid = $uid;
206
            } else {
207 40
                $pid = $updatedFields['pid'] ?? $this->getValidatedPid('pages', $uid);
208
            }
209 2
        } catch (\Throwable $e) {
210 2
            $pid = null;
211
        }
212
213 42
        if ($pid === null) {
214 2
            $this->removeFromIndexAndQueueWhenItemInQueue('pages', $uid);
215 2
            return;
216
        }
217
218 40
        $this->processPageRecord($uid, (int)$pid, $updatedFields);
219 40
    }
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 74
    protected function getSolrConfigurationFromPageId(int $pageId): TypoScriptConfiguration
357
    {
358 74
        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 54
    protected function processPageRecord(int $uid, int $pid, array $updatedFields = []): void
390
    {
391 54
        $configurationPageId = $this->getConfigurationPageId('pages', (int)$pid, $uid);
392 54
        if ($configurationPageId === 0) {
393 1
            $this->mountPageUpdater->update($uid);
394 1
            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
            $solrConfiguration = $this->getSolrConfigurationFromPageId($configurationPageId);
426 66
            $isMonitoredRecord = $solrConfiguration->getIndexQueueIsMonitoredTable($recordTable);
427 66
            if (!$isMonitoredRecord) {
428
                // when it is a non monitored record, we can skip it.
429 2
                continue;
430
            }
431
432 64
            $record = $this->configurationAwareRecordService->getRecord($recordTable, $recordUid, $solrConfiguration);
433 64
            if (empty($record)) {
434
                // TODO move this part to the garbage collector
435
                // check if the item should be removed from the index because it no longer matches the conditions
436 4
                $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
437 4
                continue;
438
            }
439
            // Clear existing index queue items to prevent mount point duplicates.
440
            // This needs to be done before the overlay handling, because handling an overlay record should
441
            // not trigger a deletion.
442 62
            $isTranslation = !empty($record['sys_language_uid']) && $record['sys_language_uid'] !== 0;
443 62
            if ($recordTable === 'pages' && !$isTranslation) {
444 48
                $this->indexQueue->deleteItem('pages', $recordUid);
445
            }
446
447
            // only update/insert the item if we actually found a record
448 62
            $isLocalizedRecord = $this->tcaService->isLocalizedRecord($recordTable, $record);
449 62
            $recordUid = $this->tcaService->getTranslationOriginalUidIfTranslated($recordTable, $record, $recordUid);
450
451 62
            if ($isLocalizedRecord && !$this->getIsTranslationParentRecordEnabled($recordTable, $recordUid)) {
452
                // we have a localized record without a visible parent record. Nothing to do.
453
                continue;
454
            }
455
456 62
            if ($this->tcaService->isEnabledRecord($recordTable, $record)) {
457 56
                $this->indexQueue->updateItem($recordTable, $recordUid);
458
            }
459
        }
460 66
    }
461
462
    /**
463
     * This method is used to determine the pageId that should be used to retrieve the index queue configuration.
464
     *
465
     * @param string $recordTable
466
     * @param int $recordPageId
467
     * @param int $recordUid
468
     * @return int
469
     */
470 54
    protected function getConfigurationPageId(string $recordTable, int $recordPageId, int $recordUid): int
471
    {
472 54
        $rootPageId = $this->rootPageResolver->getRootPageId($recordPageId);
473 54
        if ($this->rootPageResolver->getIsRootPageId($rootPageId)) {
474 53
            return $recordPageId;
475
        }
476
477 1
        $alternativeSiteRoots = $this->rootPageResolver->getAlternativeSiteRootPagesIds(
478 1
            $recordTable,
479
            $recordUid,
480
            $recordPageId
481
        );
482 1
        return (int)array_pop($alternativeSiteRoots);
483
    }
484
485
    /**
486
     * Checks if the parent record of the translated record is enabled.
487
     *
488
     * @param string $recordTable
489
     * @param int $recordUid
490
     * @return bool
491
     */
492 2
    protected function getIsTranslationParentRecordEnabled(string $recordTable, int $recordUid): bool
493
    {
494 2
        $l10nParentRecord = (array)BackendUtility::getRecord($recordTable, $recordUid, '*', '', false);
495 2
        return $this->tcaService->isEnabledRecord($recordTable, $l10nParentRecord);
496
    }
497
498
    /**
499
     * Applies the updateItem instruction on a collection of pageIds.
500
     *
501
     * @param array $treePageIds
502
     */
503 12
    protected function updatePageIdItems(array $treePageIds): void
504
    {
505 12
        foreach ($treePageIds as $treePageId) {
506 10
            $this->indexQueue->updateItem('pages', $treePageId);
507
        }
508 12
    }
509
510
    /**
511
     * Triggers Index Queue updates for other pages showing content from the
512
     * page currently being updated.
513
     *
514
     * @param int $pageId UID of the page currently being updated
515
     */
516 53
    protected function updateCanonicalPages(int $pageId): void
517
    {
518 53
        $canonicalPages = $this->pagesRepository->findPageUidsWithContentsFromPid((int)$pageId);
519 53
        foreach ($canonicalPages as $page) {
520
            $this->indexQueue->updateItem('pages', $page['uid']);
521
        }
522 53
    }
523
524
    /**
525
     * Retrieves the pid of a record, returns null if no pid could be found
526
     *
527
     * @param string $table
528
     * @param int $uid
529
     * @return int|null
530
     */
531 34
    protected function getValidatedPid(string $table, int $uid): ?int
532
    {
533 34
        $pid = $this->dataHandler->getPID($table, $uid);
534 34
        if ($pid === false) {
535 1
            $message = 'Record without valid pid was processed ' . $table . ':' . $uid;
536 1
            $this->logger->log(SolrLogManager::WARNING, $message);
537 1
            return null;
538
        }
539
540 33
        return (int)$pid;
541
    }
542
543
    /**
544
     * @return GarbageHandler
545
     */
546 6
    protected function getGarbageHandler(): GarbageHandler
547
    {
548 6
        return GeneralUtility::makeInstance(GarbageHandler::class);
549
    }
550
}
551