Completed
Push — master ( 46a0bd...244769 )
by Christian
03:39
created

RefIndex::getDeletableRecUidListFromTable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 2.0008

Importance

Changes 0
Metric Value
cc 2
eloc 19
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 29
ccs 16
cts 17
cp 0.9412
crap 2.0008
rs 9.6333
1
<?php
2
namespace Aoe\UpdateRefindex\Typo3;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2020 AOE GmbH <[email protected]>
8
 *
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 2 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use Exception;
29
use PDO;
30
use TYPO3\CMS\Core\Database\Connection;
31
use TYPO3\CMS\Core\Database\ConnectionPool;
32
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
33
use TYPO3\CMS\Core\Database\ReferenceIndex;
34
use TYPO3\CMS\Core\Utility\GeneralUtility;
35
36
/**
37
 * scheduler-task to update refindex of TYPO3
38
 *
39
 * @package update_refindex
40
 * @subpackage Typo3
41
 */
42
class RefIndex
43
{
44
    const ARRAY_CHUNK_SIZE = 100;
45
46
    /**
47
     * @var ConnectionPool
48
     */
49
    private $connectionPool;
50
51
    /**
52
     * @var array
53
     */
54
    private $existingTables;
55
56
    /**
57
     * @var array
58
     */
59
    private $selectedTables = [];
60
61
    /**
62
     * @var ReferenceIndex
63
     */
64
    private $referenceIndex;
65
66
    /**
67
     * @param array $selectedTables
68
     * @return RefIndex
69
     */
70 2
    public function setSelectedTables(array $selectedTables)
71
    {
72 2
        $this->selectedTables = $selectedTables;
73
74 2
        return $this;
75
    }
76
77
    /**
78
     * @return array
79
     */
80 2
    public function getSelectedTables()
81
    {
82 2
        return $this->selectedTables;
83
    }
84
85
    /**
86
     * @return array
87
     */
88 1
    public function getExistingTables()
89
    {
90 1
        if ($this->existingTables === null) {
91 1
            $this->existingTables = array_keys($GLOBALS['TCA']);
92 1
            sort($this->existingTables);
93
        }
94
95 1
        return $this->existingTables;
96
    }
97
98
    /**
99
     * update refIndex of selected tables
100
     */
101 2
    public function update()
102
    {
103
        // update index of selected tables
104 2
        foreach ($this->getSelectedTables() as $selectedTable) {
105 2
            if (array_search($selectedTable, $this->getExistingTables()) !== false) {
106 1
                $this->updateTable($selectedTable);
107
            }
108
        }
109
110
        // delete lost indexes ONLY if index of ALL tables where updated
111 2
        if (count($this->getExistingTables()) === count($this->getSelectedTables())) {
112 1
            $this->deleteLostIndexes();
113
        }
114 2
    }
115
116
    /**
117
     * Searching lost indexes for non-existing tables
118
     * this code is inspired by the code of method 'updateIndex' in class '\TYPO3\CMS\Core\Database\ReferenceIndex'
119
     */
120 1
    protected function deleteLostIndexes()
121
    {
122 1
        $queryBuilder = $this->getQueryBuilderForTable('sys_refindex');
123
        $queryBuilder
124 1
            ->delete('sys_refindex')
125 1
            ->where(
126 1
                $queryBuilder->expr()->notIn(
127 1
                    'tablename',
128 1
                    $queryBuilder->createNamedParameter($this->getExistingTables(), Connection::PARAM_STR_ARRAY)
129
                )
130
            );
131 1
        $queryBuilder->execute();
132 1
    }
133
134
    /**
135
     * update table
136
     * this code is inspired by the code of method 'updateIndex' in class '\TYPO3\CMS\Core\Database\ReferenceIndex'
137
     *
138
     * @param string $tableName
139
     */
140 1
    protected function updateTable($tableName)
141
    {
142
        // Select all records from table, including deleted records
143 1
        $queryBuilder = $this->getQueryBuilderForTable($tableName);
144
        $allRecs = $queryBuilder
145 1
            ->select('uid')
146 1
            ->from($tableName)
147 1
            ->execute()
148 1
            ->fetchAll(PDO::FETCH_ASSOC);
149
150
        // Update refindex table for all records in table
151 1
        foreach ($allRecs as $recdat) {
152 1
            $this->getReferenceIndex()->updateRefIndexTable($tableName, $recdat['uid']);
153
        }
154
155 1
        $recUidList = $this->getDeletableRecUidListFromTable($tableName);
156
157 1
        if (!empty($recUidList)) {
158
            // Searching lost indexes for this table:
159 1
            $queryBuilder = $this->getQueryBuilderForTable('sys_refindex');
160 1
            foreach (array_chunk($recUidList, self::ARRAY_CHUNK_SIZE) as $recUidChunk) {
161
                $queryBuilder
162 1
                    ->delete('sys_refindex')
163 1
                    ->where(
164 1
                        $queryBuilder->expr()->eq(
165 1
                            'tablename',
166 1
                            $queryBuilder->createNamedParameter($tableName, PDO::PARAM_STR)
167
                        )
168
                    )
169 1
                    ->andWhere(
170 1
                        $queryBuilder->expr()->in(
171 1
                            'recuid',
172 1
                            $queryBuilder->createNamedParameter($recUidChunk, Connection::PARAM_INT_ARRAY)
173
                        )
174
                    );
175 1
                $queryBuilder->execute();
176
            }
177
        }
178 1
    }
179
180
    /**
181
     * @param string $tableName
182
     *
183
     * @return int[]
184
     */
185 1
    protected function getDeletableRecUidListFromTable($tableName): array
186
    {
187
        // Select all records from table, including deleted records
188 1
        $subQueryBuilder = $this->getQueryBuilderForTable($tableName);
189
        $subQueryBuilder
190 1
            ->select('uid')
191 1
            ->from($tableName);
192
193
        // Select all records from sys_refindex which are not in $tableName, including deleted records
194 1
        $queryBuilder = $this->getQueryBuilderForTable('sys_refindex');
195
        $queryBuilder
196 1
            ->select('recuid')
197 1
            ->from('sys_refindex')
198 1
            ->where(
199 1
                $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($tableName, PDO::PARAM_STR))
200
            )
201 1
            ->andWhere($queryBuilder->expr()->notIn('recuid', $subQueryBuilder->getSQL()))
202 1
            ->groupBy('recuid');
203
204
        $allRecs = $queryBuilder
205 1
            ->execute()
206 1
            ->fetchAll(PDO::FETCH_ASSOC);
207
208 1
        $recUidList = [0];
209 1
        foreach ($allRecs as $recdat) {
210
            $recUidList[] = (int)$recdat['recuid'];
211
        }
212
213 1
        return $recUidList;
214
    }
215
216
    /**
217
     * @return ReferenceIndex
218
     */
219 1
    protected function getReferenceIndex(): ReferenceIndex
220
    {
221 1
        if (null === $this->referenceIndex) {
222 1
            $this->referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
223
        }
224
225 1
        return $this->referenceIndex;
226
    }
227
228
    /**
229
     * @param string $table
230
     * @param bool   $useEnableFields
231
     * @return QueryBuilder
232
     */
233 3
    private function getQueryBuilderForTable(string $table, bool $useEnableFields = false): QueryBuilder
234
    {
235 3
        $connectionPool = $this->getConnectionPool();
236 3
        $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
237
238 3
        if (false === $useEnableFields) {
239 3
            $queryBuilder->getRestrictions()->removeAll();
240
        }
241
242 3
        return $queryBuilder;
243
    }
244
245
    /**
246
     * @return ConnectionPool
247
     */
248 3
    private function getConnectionPool(): ConnectionPool
249
    {
250 3
        if (null === $this->connectionPool) {
251 3
            $this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
252
        }
253
254 3
        return $this->connectionPool;
255
    }
256
}
257