Passed
Pull Request — release-11.5.x (#3193)
by Markus
44:19
created

DataUpdateHandler::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

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
    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
        parent::__construct($recordService, $frontendEnvironment, $tcaService, $indexQueue);
148
149
        $this->mountPageUpdater = $mountPageUpdater;
150
        $this->rootPageResolver = $rootPageResolver;
151
        $this->pagesRepository = $pagesRepository;
152
        $this->dataHandler = $dataHandler;
153
        $this->logger = $solrLogManager ?? GeneralUtility::makeInstance(
154
            SolrLogManager::class,
155
            /** @scrutinizer ignore-type */ __CLASS__
156
        );
157
    }
158
159
    /**
160
     * Handle content element update
161
     *
162
     * @param int $uid
163
     * @param array $updatedFields
164
     */
165
    public function handleContentElementUpdate(int $uid, array $updatedFields = []): void
166
    {
167
        $pid = $updatedFields['pid'] ?? $this->getValidatedPid('tt_content', $uid);
168
        if ($pid === null) {
169
            return;
170
        }
171
172
        $this->processPageRecord($pid, (int)$pid, $updatedFields);
173
    }
174
175
    /**
176
     * Handles the deletion of a content element
177
     *
178
     * @param int $uid
179
     */
180
    public function handleContentElementDeletion(int $uid): void
181
    {
182
        // @TODO: Should be checked, is possibly unnecessary as
183
        //        also done via GarbageCollector & PageStrategy
184
185
        $pid = $this->getValidatedPid('tt_content', $uid);
186
        if ($pid === null) {
187
            return;
188
        }
189
190
        $this->indexQueue->updateItem('pages', $pid, Util::getExectionTime());
191
    }
192
193
    /**
194
     * Handles page updates
195
     *
196
     * @param int $uid
197
     * @param array $updatedFields
198
     */
199
    public function handlePageUpdate(int $uid, array $updatedFields = []): void
200
    {
201
        try {
202
            if (isset($updatedFields['l10n_parent']) && intval($updatedFields['l10n_parent']) > 0) {
203
                $pid = $updatedFields['l10n_parent'];
204
            } elseif ($this->rootPageResolver->getIsRootPageId($uid)) {
205
                $pid = $uid;
206
            } else {
207
                $pid = $updatedFields['pid'] ?? $this->getValidatedPid('pages', $uid);
208
            }
209
        } catch (\Throwable $e) {
210
            $pid = null;
211
        }
212
213
        if ($pid === null) {
214
            $this->removeFromIndexAndQueueWhenItemInQueue('pages', $uid);
215
            return;
216
        }
217
218
        $this->processPageRecord($uid, (int)$pid, $updatedFields);
219
    }
220
221
    /**
222
     * Handles record updates
223
     *
224
     * @param int $uid
225
     * @param string $table
226
     */
227
    public function handleRecordUpdate(int $uid, string $table): void
228
    {
229
        $rootPageIds = $this->getRecordRootPageIds($table, $uid);
230
        $this->processRecord($table, $uid, $rootPageIds);
231
    }
232
233
    /**
234
     * Handles a version swap
235
     *
236
     * @param int $uid
237
     * @param string $table
238
     */
239
    public function handleVersionSwap(int $uid, string $table): void
240
    {
241
        $isPageRelatedRecord = ($table === 'tt_content' || $table === 'pages');
242
        if($isPageRelatedRecord) {
243
            $uid = ($table === 'tt_content' ? $this->getValidatedPid($table, $uid) : $uid);
244
            if ($uid === null) {
245
                return;
246
            }
247
            $this->applyPageChangesToQueue($uid);
248
        } else {
249
            $recordPageId = $this->getValidatedPid($table, $uid);
250
            if ($recordPageId === null) {
251
                return;
252
            }
253
            $this->applyRecordChangesToQueue($table, $uid, $recordPageId);
254
        }
255
    }
256
257
    /**
258
     * Handle page move
259
     *
260
     * @param int $uid
261
     */
262
    public function handleMovedPage(int $uid): void
263
    {
264
        $this->applyPageChangesToQueue($uid);
265
    }
266
267
    /**
268
     * Handle record move
269
     *
270
     * @param int $uid
271
     * @param string $table
272
     */
273
    public function handleMovedRecord(int $uid, string $table): void
274
    {
275
        $pid = $this->getValidatedPid($table, $uid);
276
        if ($pid === null) {
277
            return;
278
        }
279
280
        $this->applyRecordChangesToQueue($table, $uid, $pid);
281
    }
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
    protected function applyPageChangesToQueue(int $uid): void
290
    {
291
        $solrConfiguration = $this->getSolrConfigurationFromPageId($uid);
292
        $record = $this->configurationAwareRecordService->getRecord('pages', $uid, $solrConfiguration);
293
        if (!empty($record) && $this->tcaService->isEnabledRecord('pages', $record)) {
294
            $this->mountPageUpdater->update($uid);
295
            $this->indexQueue->updateItem('pages', $uid);
296
        } else {
297
            $this->removeFromIndexAndQueueWhenItemInQueue('pages', $uid);
298
        }
299
    }
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
    protected function applyRecordChangesToQueue(string $table, int $uid, int $pid): void
309
    {
310
        $solrConfiguration = $this->getSolrConfigurationFromPageId($pid);
311
        $isMonitoredTable = $solrConfiguration->getIndexQueueIsMonitoredTable($table);
312
313
        if ($isMonitoredTable) {
314
            $record = $this->configurationAwareRecordService->getRecord($table, $uid, $solrConfiguration);
315
316
            if (!empty($record) && $this->tcaService->isEnabledRecord($table, $record)) {
317
                $uid = $this->tcaService->getTranslationOriginalUidIfTranslated($table, $record, $uid);
318
                $this->indexQueue->updateItem($table, $uid);
319
            } else {
320
                // TODO should be moved to garbage collector
321
                $this->removeFromIndexAndQueueWhenItemInQueue($table, $uid);
322
            }
323
        }
324
    }
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
    protected function removeFromIndexAndQueue(string $recordTable, int $recordUid): void
333
    {
334
        $this->getGarbageHandler()->collectGarbage($recordTable, $recordUid);
335
    }
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
    protected function removeFromIndexAndQueueWhenItemInQueue(string $recordTable, int $recordUid): void
344
    {
345
        if (!$this->indexQueue->containsItem($recordTable, $recordUid)) {
346
            return;
347
        }
348
349
        $this->removeFromIndexAndQueue($recordTable, $recordUid);
350
    }
351
352
    /**
353
     * @param $pageId
354
     * @return TypoScriptConfiguration
355
     */
356
    protected function getSolrConfigurationFromPageId(int $pageId): TypoScriptConfiguration
357
    {
358
        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
    protected function getRecordRootPageIds(string $recordTable, int $recordUid): array
369
    {
370
        try {
371
            $rootPageIds = $this->rootPageResolver->getResponsibleRootPageIds($recordTable, $recordUid);
372
        } catch (\InvalidArgumentException $e) {
373
            $rootPageIds = [];
374
        }
375
376
        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
    protected function processPageRecord(int $uid, int $pid, array $updatedFields = []): void
390
    {
391
        $configurationPageId = $this->getConfigurationPageId('pages', (int)$pid, $uid);
392
        if ($configurationPageId === 0) {
393
            $this->mountPageUpdater->update($uid);
394
            return;
395
        }
396
        $rootPageIds = [$configurationPageId];
397
398
        $this->processRecord('pages', $uid, $rootPageIds);
399
400
        $this->updateCanonicalPages($uid);
401
        $this->mountPageUpdater->update($uid);
402
403
        $recursiveUpdateRequired = $this->isRecursivePageUpdateRequired($uid, $updatedFields);
404
        if ($recursiveUpdateRequired) {
405
            $treePageIds = $this->getSubPageIds($uid);
406
            $this->updatePageIdItems($treePageIds);
407
        }
408
    }
409
410
    /**
411
     * Process a record
412
     *
413
     * @param string $recordTable
414
     * @param int $recordUid
415
     * @param array $rootPageIds
416
     */
417
    protected function processRecord(string $recordTable, int $recordUid, array $rootPageIds): void
418
    {
419
        if (empty($rootPageIds)) {
420
            $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
421
            return;
422
        }
423
424
        foreach ($rootPageIds as $configurationPageId) {
425
            $site = $this->getSiteRepository()->getSiteByPageId($configurationPageId);
426
            if (!$site instanceof SiteInterface) {
427
                continue;
428
            }
429
            $solrConfiguration = $site->getSolrConfiguration();
430
            $isMonitoredRecord = $solrConfiguration->getIndexQueueIsMonitoredTable($recordTable);
431
            if (!$isMonitoredRecord) {
432
                // when it is a non monitored record, we can skip it.
433
                continue;
434
            }
435
436
            $record = $this->configurationAwareRecordService->getRecord($recordTable, $recordUid, $solrConfiguration);
437
            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
                $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
441
                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
            $isTranslation = !empty($record['sys_language_uid']) && $record['sys_language_uid'] !== 0;
447
            if ($recordTable === 'pages' && !$isTranslation) {
448
                $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
            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
                $recordUid = $this->tcaService->getTranslationOriginalUidIfTranslated($recordTable, $record, $recordUid);
454
            }
455
456
            // only update/insert the item if we actually found a record
457
            $isLocalizedRecord = $this->tcaService->isLocalizedRecord($recordTable, $record);
458
459
            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
            if ($this->tcaService->isEnabledRecord($recordTable, $record)) {
465
                $this->indexQueue->updateItem($recordTable, $recordUid);
466
            }
467
        }
468
    }
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
    protected function getConfigurationPageId(string $recordTable, int $recordPageId, int $recordUid): int
479
    {
480
        $rootPageId = $this->rootPageResolver->getRootPageId($recordPageId);
481
        $rootPageRecord = $this->getPagesRepository()->getPage((int)$rootPageId);
482
        if (isset($rootPageRecord['sys_language_uid'])
483
            && (int)$rootPageRecord['sys_language_uid'] > 0
484
            && isset($rootPageRecord['l10n_parent'])
485
            && (int)$rootPageRecord['l10n_parent'] > 0
486
        ) {
487
            $rootPageId = $recordPageId = $rootPageRecord['l10n_parent'];
488
        }
489
        if ($this->rootPageResolver->getIsRootPageId($rootPageId)) {
490
            return $recordPageId;
491
        }
492
493
        $alternativeSiteRoots = $this->rootPageResolver->getAlternativeSiteRootPagesIds(
494
            $recordTable,
495
            $recordUid,
496
            $recordPageId
497
        );
498
        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
    protected function getIsTranslationParentRecordEnabled(string $recordTable, int $recordUid): bool
509
    {
510
        $l10nParentRecord = (array)BackendUtility::getRecord($recordTable, $recordUid, '*', '', false);
511
        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
    protected function updatePageIdItems(array $treePageIds): void
520
    {
521
        foreach ($treePageIds as $treePageId) {
522
            $this->indexQueue->updateItem('pages', $treePageId);
523
        }
524
    }
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
    protected function updateCanonicalPages(int $pageId): void
533
    {
534
        $canonicalPages = $this->pagesRepository->findPageUidsWithContentsFromPid((int)$pageId);
535
        foreach ($canonicalPages as $page) {
536
            $this->indexQueue->updateItem('pages', $page['uid']);
537
        }
538
    }
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
    protected function getValidatedPid(string $table, int $uid): ?int
548
    {
549
        $pid = $this->dataHandler->getPID($table, $uid);
550
        if ($pid === false) {
551
            $message = 'Record without valid pid was processed ' . $table . ':' . $uid;
552
            $this->logger->log(SolrLogManager::WARNING, $message);
553
            return null;
554
        }
555
556
        return (int)$pid;
557
    }
558
559
    /**
560
     * @return GarbageHandler
561
     */
562
    protected function getGarbageHandler(): GarbageHandler
563
    {
564
        return GeneralUtility::makeInstance(GarbageHandler::class);
565
    }
566
567
    /**
568
     * @return SiteRepository
569
     */
570
    protected function getSiteRepository(): SiteRepository
571
    {
572
        return GeneralUtility::makeInstance(SiteRepository::class);
573
    }
574
}
575