Passed
Push — main ( 3b24a8...b1e1f3 )
by Felix
04:00
created

RefIndex::setSelectedTables()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
ccs 3
cts 3
cp 1
crap 1
1
<?php
2
namespace Aoe\UpdateRefindex\Typo3;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2021 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 Doctrine\DBAL\FetchMode;
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 (in_array($selectedTable, $this->getExistingTables(), true)) {
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
    }
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
                    'tablename',
128 1
                    $queryBuilder->createNamedParameter($this->getExistingTables(), Connection::PARAM_STR_ARRAY)
129
                )
130
            );
131 1
        $queryBuilder->execute();
132
    }
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
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\ForwardCom...lity\Result::fetchAll() has been deprecated: Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

144
        $allRecs = /** @scrutinizer ignore-deprecated */ $queryBuilder

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
145 1
            ->select('uid')
146 1
            ->from($tableName)
147 1
            ->execute()
148 1
            ->fetchAll(FetchMode::ASSOCIATIVE);
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
                            'tablename',
166 1
                            $queryBuilder->createNamedParameter($tableName, PDO::PARAM_STR)
167
                        )
168
                    )
169 1
                    ->andWhere(
170 1
                        $queryBuilder->expr()->in(
171
                            'recuid',
172 1
                            $queryBuilder->createNamedParameter($recUidChunk, Connection::PARAM_INT_ARRAY)
173
                        )
174
                    );
175 1
                $queryBuilder->execute();
176
            }
177
        }
178
    }
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
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\ForwardCom...lity\Result::fetchAll() has been deprecated: Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

204
        $allRecs = /** @scrutinizer ignore-deprecated */ $queryBuilder

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
205 1
            ->execute()
206 1
            ->fetchAll(FetchMode::ASSOCIATIVE);
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