Passed
Branch master (5f6eff)
by Rafael
04:03
created

AbstractDataHandlerListener::getSubPageIds()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1.0019

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 15
ccs 7
cts 8
cp 0.875
rs 10
cc 1
nc 1
nop 1
crap 1.0019
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2015-2016 Timo Schmidt <[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 ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService;
28
use TYPO3\CMS\Backend\Utility\BackendUtility;
29
use TYPO3\CMS\Core\Database\QueryGenerator;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32
/**
33
 * Changes in TYPO3 have an impact on the solr content and are caught
34
 * by the GarbageCollector and RecordMonitor. Both act as a TCE Main Hook.
35
 *
36
 * This base class is used to share functionality that are needed for both
37
 * to perform the changes in the data handler on the solr index.
38
 *
39
 * @author Timo Schmidt <[email protected]>
40
 */
41
abstract class AbstractDataHandlerListener
42
{
43
    /**
44
     * Reference to the configuration manager
45
     *
46
     * @var \ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService
47
     */
48
    protected $configurationAwareRecordService;
49
50
    /**
51
     * @var FrontendEnvironment
52
     */
53
    protected $frontendEnvironment = null;
54
55
    /**
56
     * AbstractDataHandlerListener constructor.
57
     * @param ConfigurationAwareRecordService|null $recordService
58
     */
59 47
    public function __construct(ConfigurationAwareRecordService $recordService = null, FrontendEnvironment $frontendEnvironment = null)
60
    {
61 47
        $this->configurationAwareRecordService = $recordService ?? GeneralUtility::makeInstance(ConfigurationAwareRecordService::class);
62 47
        $this->frontendEnvironment = $frontendEnvironment ?? GeneralUtility::makeInstance(FrontendEnvironment::class);
63 47
    }
64
65
    /**
66
     * @return array
67
     */
68 33
    protected function getAllRelevantFieldsForCurrentState()
69
    {
70 33
        $allCurrentStateFieldnames = [];
71
72 33
        foreach ($this->getUpdateSubPagesRecursiveTriggerConfiguration() as $triggerConfiguration) {
73 33
            if (!isset($triggerConfiguration['currentState']) || !is_array($triggerConfiguration['currentState'])) {
74
                // when no "currentState" configuration for the trigger exists we can skip it
75 33
                continue;
76
            }
77
78
            // we collect the currentState fields to return a unique list of all fields
79 33
            $allCurrentStateFieldnames = array_merge($allCurrentStateFieldnames, array_keys($triggerConfiguration['currentState']));
80
        }
81
82 33
        return array_unique($allCurrentStateFieldnames);
83
    }
84
85
    /**
86
     * When the extend to subpages flag was set, we determine the affected subpages and return them.
87
     *
88
     * @param int $pageId
89
     * @return array
90
     */
91 8
    protected function getSubPageIds($pageId)
92
    {
93
        /** @var $queryGenerator \TYPO3\CMS\Core\Database\QueryGenerator */
94 8
        $queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
95
96
        // here we retrieve only the subpages of this page because the permission clause is not evaluated
97
        // on the root node.
98 8
        $permissionClause = ' 1 ' . BackendUtility::BEenableFields('pages');
99 8
        $treePageIdList = $queryGenerator->getTreeList($pageId, 20, 0, $permissionClause);
100 8
        $treePageIds = array_map('intval', explode(',', $treePageIdList));
101
102
            // the first one can be ignored because this is the page itself
103 8
        array_shift($treePageIds);
104
105 8
        return $treePageIds;
106
    }
107
108
    /**
109
     * Checks if a page update will trigger a recursive update of pages
110
     *
111
     * This can either be the case if some $changedFields are part of the RecursiveUpdateTriggerConfiguration or
112
     * columns have explicitly been configured via plugin.tx_solr.index.queue.recursiveUpdateFields
113
     *
114
     * @param int $pageId
115
     * @param array $changedFields
116
     * @return bool
117
     */
118 33
    protected function isRecursivePageUpdateRequired($pageId, $changedFields)
119
    {
120
        // First check RecursiveUpdateTriggerConfiguration
121 33
        $isRecursiveUpdateRequired = $this->isRecursiveUpdateRequired($pageId, $changedFields);
122
        // If RecursiveUpdateTriggerConfiguration is false => check if changeFields are part of recursiveUpdateFields
123 33
        if ($isRecursiveUpdateRequired === false) {
124 28
            $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($pageId);
125 28
            $indexQueueConfigurationName = $this->configurationAwareRecordService->getIndexingConfigurationName('pages', $pageId, $solrConfiguration);
126 28
            if ($indexQueueConfigurationName === null) {
127 2
                return false;
128
            }
129 26
            $updateFields = $solrConfiguration->getIndexQueueConfigurationRecursiveUpdateFields($indexQueueConfigurationName);
130
131
            // Check if no additional fields have been defined and then skip recursive update
132 26
            if (empty($updateFields)) {
133 20
                return false;
134
            }
135
            // If the recursiveUpdateFields configuration is not part of the $changedFields skip recursive update
136 6
            if (!array_intersect_key($changedFields, $updateFields)) {
137 3
                return false;
138
            }
139
        }
140
141 8
        return true;
142
    }
143
144
    /**
145
     * @param int $pageId
146
     * @param array $changedFields
147
     * @return bool
148
     */
149 33
    protected function isRecursiveUpdateRequired($pageId, $changedFields)
150
    {
151 33
        $fieldsForCurrentState = $this->getAllRelevantFieldsForCurrentState();
152 33
        $fieldListToRetrieve = implode(',', $fieldsForCurrentState);
153 33
        $page = BackendUtility::getRecord('pages', $pageId, $fieldListToRetrieve, '', false);
154 33
        foreach ($this->getUpdateSubPagesRecursiveTriggerConfiguration() as $triggerConfiguration) {
155 33
            $allCurrentStateFieldsMatch = $this->getAllCurrentStateFieldsMatch($triggerConfiguration, $page);
156 33
            $allChangeSetValuesMatch = $this->getAllChangeSetValuesMatch($triggerConfiguration, $changedFields);
157
158 33
            $aMatchingTriggerHasBeenFound = $allCurrentStateFieldsMatch && $allChangeSetValuesMatch;
159 33
            if ($aMatchingTriggerHasBeenFound) {
160 5
                return true;
161
            }
162
        }
163
164 28
        return false;
165
    }
166
167
    /**
168
     * @param array $triggerConfiguration
169
     * @param array $pageRecord
170
     * @return bool
171
     */
172 33
    protected function getAllCurrentStateFieldsMatch($triggerConfiguration, $pageRecord)
173
    {
174 33
        $triggerConfigurationHasNoCurrentStateConfiguration = !array_key_exists('currentState', $triggerConfiguration);
175 33
        if ($triggerConfigurationHasNoCurrentStateConfiguration) {
176 31
            return true;
177
        }
178 32
        $diff = array_diff_assoc($triggerConfiguration['currentState'], $pageRecord);
179 32
        return empty($diff);
180
    }
181
182
    /**
183
     * @param array $triggerConfiguration
184
     * @param array $changedFields
185
     * @return bool
186
     */
187 33
    protected function getAllChangeSetValuesMatch($triggerConfiguration, $changedFields)
188
    {
189 33
        $triggerConfigurationHasNoChangeSetStateConfiguration = !array_key_exists('changeSet', $triggerConfiguration);
190 33
        if ($triggerConfigurationHasNoChangeSetStateConfiguration) {
191
            return true;
192
        }
193
194 33
        $diff = array_diff_assoc($triggerConfiguration['changeSet'], $changedFields);
195 33
        return empty($diff);
196
    }
197
198
    /**
199
     * The implementation of this method need to retrieve a configuration to determine which record data
200
     * and change combination required a recursive change.
201
     *
202
     * The structure needs to be:
203
     *
204
     * [
205
     *      [
206
     *           'currentState' => ['fieldName1' => 'value1'],
207
     *           'changeSet' => ['fieldName1' => 'value1']
208
     *      ]
209
     * ]
210
     *
211
     * When the all values of the currentState AND all values of the changeSet match, a recursive update
212
     * will be triggered.
213
     *
214
     * @return array
215
     */
216
    abstract protected function getUpdateSubPagesRecursiveTriggerConfiguration();
217
}
218