Failed Conditions
Pull Request — release-11.1.x (#3164)
by
unknown
17:11
created

isInvisibleByStartOrEndtime()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
cc 3
nc 3
nop 2
crap 12
1
<?php
2
namespace ApacheSolrForTypo3\Solr\IndexQueue;
3
4
5
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService;
6
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\RootPageResolver;
7
use ApacheSolrForTypo3\Solr\Event\BeforeDomainObjectObserverUpdate;
8
use ApacheSolrForTypo3\Solr\FrontendEnvironment;
9
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
10
use ApacheSolrForTypo3\Solr\System\TCA\TCAService;
11
use ApacheSolrForTypo3\Solr\Util;
12
use Psr\EventDispatcher\EventDispatcherInterface;
13
use TYPO3\CMS\Core\Utility\GeneralUtility;
14
use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
15
use TYPO3\CMS\Extbase\Object\ObjectManager;
16
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
17
18
/**
19
 * A class that monitors Extbase domain objects for changing and removal so that the
20
 * changed record gets passed to the index queue to update the according index document
21
 * and invisible or deleted records are handled by the garbage collector.
22
 *
23
 * @package TYPO3
24
 * @subpackage solr
25
 */
26
class DomainObjectObserver
27
{
28
29
    /**
30
     * @var \ApacheSolrForTypo3\Solr\IndexQueue\Queue
31
     */
32
    protected $indexQueue;
33
34
    /**
35
     * @var \ApacheSolrForTypo3\Solr\IndexQueue\DomainObjectObserverRegistry
36
     */
37
    protected $domainObjectObserverRegistry;
38
39
    /**
40
     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
41
     */
42
    protected $dataMapper;
43
44
    /**
45
     * @var array
46
     */
47
    static protected $classToTableNameMap = [];
48
49
    /**
50
     * @var TCAService
51
     */
52
    protected $tcaService;
53
54
    /**
55
     * RootPageResolver
56
     *
57
     * @var RootPageResolver
58
     */
59
    protected $rootPageResolver;
60
61
    /**
62
     * Reference to the configuration manager
63
     *
64
     * @var ConfigurationAwareRecordService
65
     */
66
    protected $configurationAwareRecordService;
67
68
    /**
69
     * Unsupported tables
70
     *
71
     * @var array
72
     */
73
    protected $ignoredTableNames = ['pages', 'tt_content'];
74
75
    /**
76
     * Constructor
77
     */
78
    public function __construct(
79
        Queue $indexQueue = null,
80
        DomainObjectObserverRegistry $domainObjectObserverRegistry = null,
81
        TCAService $TCAService = null,
82
        RootPageResolver $rootPageResolver = null,
83
        ConfigurationAwareRecordService $recordService = null,
84
        FrontendEnvironment $frontendEnvironment = null,
85
        DataMapper $dataMapper = null
86
    )
87
    {
88
        $this->indexQueue = $indexQueue ?? GeneralUtility::makeInstance(Queue::class);
89
        $this->domainObjectObserverRegistry = $domainObjectObserverRegistry ?? GeneralUtility::makeInstance(DomainObjectObserverRegistry::class);
90
        $this->tcaService = $TCAService ?? GeneralUtility::makeInstance(TCAService::class);
91
        $this->rootPageResolver = $rootPageResolver ?? GeneralUtility::makeInstance(RootPageResolver::class);
92
        $this->configurationAwareRecordService = $recordService ?? GeneralUtility::makeInstance(ConfigurationAwareRecordService::class);
93
        $this->frontendEnvironment = $frontendEnvironment ?? GeneralUtility::makeInstance(FrontendEnvironment::class);
0 ignored issues
show
Bug Best Practice introduced by
The property frontendEnvironment does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
94
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
95
        $this->dataMapper = $dataMapper ?? $objectManager->get(DataMapper::class);
96
    }
97
98
    /**
99
     * Affects new and updated objects
100
     *
101
     * @param DomainObjectInterface $object
102
     */
103
    public function afterUpdateObject($object)
104
    {
105
        $forcedChangeTime = 0;
106
107
        // TODO: has to be changed when DI is enabled
108
        $eventDispatcher = GeneralUtility::getContainer()->get(EventDispatcherInterface::class);
109
        $event = $eventDispatcher->dispatch(
110
            new BeforeDomainObjectObserverUpdate($object, $forcedChangeTime)
111
        );
112
        $object = $event->getObject();
113
        $forcedChangeTime = $event->getForcedChangeTime();
114
115
        if (!$this->isDomainObjectRegistered($object)) {
116
            return;
117
        }
118
119
        $recordTable = $this->getTableName($object);
120
        $recordUid = $object->getUid();
121
122
        try {
123
            $rootPageIds = $this->rootPageResolver->getResponsibleRootPageIds($recordTable, $recordUid);
124
            if (empty($rootPageIds)) {
125
                $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
126
                return;
127
            }
128
        } catch ( \InvalidArgumentException $e) {
129
            $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
130
            return;
131
        }
132
133
        foreach ($rootPageIds as $configurationPageId) {
134
            $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($configurationPageId);
135
136
            if ($this->ignoreRecord($recordTable, $recordUid, $solrConfiguration)) {
137
                return;
138
            }
139
140
            $record = $this->configurationAwareRecordService->getRecord($recordTable, $recordUid,
141
                $solrConfiguration);
142
143
            if (empty($record)) {
144
                $this->removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid);
145
                return;
146
            }
147
148
            //  run garbage first (e.g. important when the starttime changed)
149
            if ($this->tcaService->isHidden($recordTable, $record)
150
                || $this->isInvisibleByStartOrEndtime($recordTable, $record)
151
            ) {
152
                $this->removeFromIndexAndQueue($recordTable, $recordUid);
153
            }
154
            // then update or readd the item
155
            if ($this->tcaService->isEnabledRecord($recordTable, $record)) {
156
                $this->indexQueue->updateItem($recordTable, $recordUid, $forcedChangeTime);
157
            }
158
        }
159
    }
160
161
    /**
162
     * @param DomainObjectInterface $object
163
     */
164
    public function afterRemoveObject($object)
165
    {
166
        if (!$this->isDomainObjectRegistered($object)) {
167
            return;
168
        }
169
170
        $recordTableName = $this->getTableName($object);
171
        $recordUid = $object->getUid();
172
        $recordPageId = $object->getPid();
173
174
        $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($recordPageId);
175
176
        if ($this->ignoreRecord($recordTableName, $recordUid, $solrConfiguration)) {
177
            return;
178
        }
179
180
        $this->removeFromIndexAndQueue($recordTableName, $recordUid);
181
    }
182
183
    /**
184
     * @param $recordTableName
185
     * @param $recordUid
186
     * @param $solrConfiguration TypoScriptConfiguration
187
     * @return bool
188
     */
189
    protected function ignoreRecord($recordTableName, $recordUid, $solrConfiguration)
190
    {
191
        $ignore = false;
192
        if (in_array($recordTableName, $this->ignoredTableNames)) {
193
            $ignore = true;
194
        } elseif (Util::isDraftRecord($recordTableName, $recordUid)) {
195
            // skip workspaces: index only LIVE workspace
196
            $ignore = true;
197
        } else {
198
            $isMonitoredRecord = $solrConfiguration->getIndexQueueIsMonitoredTable($recordTableName);
199
200
            if (!$isMonitoredRecord) {
201
                // when it is a non monitored record, we can skip it.
202
                $ignore = true;
203
            }
204
        }
205
        return $ignore;
206
    }
207
208
    /**
209
     * @param $object
210
     * @return string
211
     * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception
212
     */
213
    protected function getTableName($object)
214
    {
215
        $className = get_class($object);
216
        if (!isset(self::$classToTableNameMap[$className])) {
217
            // we rely on the datamap here, because it is already correct build in extbase backend
218
            // just before the signal is called
219
            $dataMap = $this->dataMapper->getDataMap($className);
220
            self::$classToTableNameMap[$className] = $dataMap->getTableName();
221
        }
222
        return self::$classToTableNameMap[$className];
223
    }
224
225
    /**
226
     * @param $object
227
     * @return bool
228
     */
229
    protected function isDomainObjectRegistered($object)
230
    {
231
        return $this->domainObjectObserverRegistry->isRegistered(get_class($object));
232
    }
233
234
    /**
235
     * TODO - copied and adapted (since pages and tt_content are not supported anyway) from GarbageCollector
236
     *
237
     * Check if a record is getting invisible due to changes in start or endtime. In addition it is checked that the related
238
     * queue item was marked as indexed.
239
     *
240
     * @param string $table
241
     * @param array $record
242
     * @return bool
243
     */
244
    protected function isInvisibleByStartOrEndtime($table, $record)
245
    {
246
        return ($this->tcaService->isStartTimeInFuture($table, $record) || $this->tcaService->isEndTimeInPast($table, $record)) &&
247
            $this->indexQueue->containsIndexedItem($table, $record['uid']);
248
    }
249
250
    /**
251
     * TODO - copied from RecordMonitor
252
     *
253
     * Removes record from the index queue and from the solr index
254
     *
255
     * @param string $recordTable Name of table where the record lives
256
     * @param int $recordUid Id of record
257
     */
258
    protected function removeFromIndexAndQueue($recordTable, $recordUid)
259
    {
260
        $garbageCollector = GeneralUtility::makeInstance(\ApacheSolrForTypo3\Solr\GarbageCollector::class);
261
        $garbageCollector->collectGarbage($recordTable, $recordUid);
262
    }
263
    /**
264
     * TODO - copied from RecordMonitor
265
     *
266
     * Removes record from the index queue and from the solr index when the item is in the queue.
267
     *
268
     * @param string $recordTable Name of table where the record lives
269
     * @param int $recordUid Id of record
270
     */
271
    protected function removeFromIndexAndQueueWhenItemInQueue($recordTable, $recordUid)
272
    {
273
        if (!$this->indexQueue->containsItem($recordTable, $recordUid)) {
274
            return;
275
        }
276
277
        $this->removeFromIndexAndQueue($recordTable, $recordUid);
278
    }
279
}
280