Passed
Push — main ( 3301a9...d2d929 )
by Felix
15:59 queued 13:13
created

RefIndex::getDeletableRecUidListFromTable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 2.0006

Importance

Changes 0
Metric Value
cc 2
eloc 20
nc 2
nop 1
dl 0
loc 30
ccs 17
cts 18
cp 0.9444
crap 2.0006
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
namespace Aoe\UpdateRefindex\Typo3;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2021 AOE GmbH <[email protected]>
9
 *
10
 *  All rights reserved
11
 *
12
 *  This script is part of the TYPO3 project. The TYPO3 project is
13
 *  free software; you can redistribute it and/or modify
14
 *  it under the terms of the GNU General Public License as published by
15
 *  the Free Software Foundation; either version 2 of the License, or
16
 *  (at your option) any later version.
17
 *
18
 *  The GNU General Public License can be found at
19
 *  http://www.gnu.org/copyleft/gpl.html.
20
 *
21
 *  This script is distributed in the hope that it will be useful,
22
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 *  GNU General Public License for more details.
25
 *
26
 *  This copyright notice MUST APPEAR in all copies of the script!
27
 ***************************************************************/
28
29
use Doctrine\DBAL\FetchMode;
30
use PDO;
31
use TYPO3\CMS\Core\Database\Connection;
32
use TYPO3\CMS\Core\Database\ConnectionPool;
33
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
34
use TYPO3\CMS\Core\Database\ReferenceIndex;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
37
/**
38
 * scheduler-task to update refindex of TYPO3
39
 *
40
 * @package update_refindex
41
 * @subpackage Typo3
42
 */
43
class RefIndex
44
{
45
    public const ARRAY_CHUNK_SIZE = 100;
46
47
    /**
48
     * @var ConnectionPool
49
     */
50
    private $connectionPool;
51
52
    /**
53
     * @var array
54
     */
55
    private $existingTables;
56
57
    /**
58
     * @var array
59
     */
60
    private $selectedTables = [];
61
62
    /**
63
     * @var ReferenceIndex
64
     */
65
    private $referenceIndex;
66
67
    /**
68
     * @param array $selectedTables
69
     * @return RefIndex
70
     */
71 2
    public function setSelectedTables(array $selectedTables)
72
    {
73 2
        $this->selectedTables = $selectedTables;
74
75 2
        return $this;
76
    }
77
78
    /**
79
     * @return array
80
     */
81 2
    public function getSelectedTables()
82
    {
83 2
        return $this->selectedTables;
84
    }
85
86
    /**
87
     * @return array
88
     */
89 1
    public function getExistingTables()
90
    {
91 1
        if ($this->existingTables === null) {
92 1
            $this->existingTables = array_keys($GLOBALS['TCA']);
93 1
            sort($this->existingTables);
94
        }
95
96 1
        return $this->existingTables;
97
    }
98
99
    /**
100
     * update refIndex of selected tables
101
     */
102 2
    public function update()
103
    {
104
        // update index of selected tables
105 2
        foreach ($this->getSelectedTables() as $selectedTable) {
106 2
            if (in_array($selectedTable, $this->getExistingTables(), true)) {
107 1
                $this->updateTable($selectedTable);
108
            }
109
        }
110
111
        // delete lost indexes ONLY if index of ALL tables where updated
112 2
        if (count($this->getExistingTables()) === count($this->getSelectedTables())) {
113 1
            $this->deleteLostIndexes();
114
        }
115 2
    }
116
117
    /**
118
     * Searching lost indexes for non-existing tables
119
     * this code is inspired by the code of method 'updateIndex' in class '\TYPO3\CMS\Core\Database\ReferenceIndex'
120
     */
121 1
    protected function deleteLostIndexes()
122
    {
123 1
        $queryBuilder = $this->getQueryBuilderForTable('sys_refindex');
124
        $queryBuilder
125 1
            ->delete('sys_refindex')
126 1
            ->where(
127 1
                $queryBuilder->expr()
0 ignored issues
show
Bug introduced by
$queryBuilder->expr()->n...tion::PARAM_STR_ARRAY)) of type string is incompatible with the type Doctrine\DBAL\Query\Expr...on|array<integer,mixed> expected by parameter $predicates of TYPO3\CMS\Core\Database\...y\QueryBuilder::where(). ( Ignorable by Annotation )

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

127
                /** @scrutinizer ignore-type */ $queryBuilder->expr()
Loading history...
128 1
                    ->notIn(
129 1
                        'tablename',
130 1
                        $queryBuilder->createNamedParameter($this->getExistingTables(), Connection::PARAM_STR_ARRAY)
131
                    )
132
            );
133 1
        $queryBuilder->execute();
134 1
    }
135
136
    /**
137
     * update table
138
     * this code is inspired by the code of method 'updateIndex' in class '\TYPO3\CMS\Core\Database\ReferenceIndex'
139
     *
140
     * @param string $tableName
141
     */
142 1
    protected function updateTable($tableName)
143
    {
144
        // Select all records from table, including deleted records
145 1
        $queryBuilder = $this->getQueryBuilderForTable($tableName);
146
        $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

146
        $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...
147 1
            ->select('uid')
148 1
            ->from($tableName)
149 1
            ->execute()
150 1
            ->fetchAll(FetchMode::ASSOCIATIVE);
151
152
        // Update refindex table for all records in table
153 1
        foreach ($allRecs as $recdat) {
154 1
            $this->getReferenceIndex()
155 1
                ->updateRefIndexTable($tableName, $recdat['uid']);
156
        }
157
158 1
        $recUidList = $this->getDeletableRecUidListFromTable($tableName);
159
160 1
        if (!empty($recUidList)) {
161
            // Searching lost indexes for this table:
162 1
            $queryBuilder = $this->getQueryBuilderForTable('sys_refindex');
163 1
            foreach (array_chunk($recUidList, self::ARRAY_CHUNK_SIZE) as $recUidChunk) {
164
                $queryBuilder
165 1
                    ->delete('sys_refindex')
166 1
                    ->where(
167 1
                        $queryBuilder->expr()
0 ignored issues
show
Bug introduced by
$queryBuilder->expr()->e...eName, PDO::PARAM_STR)) of type string is incompatible with the type Doctrine\DBAL\Query\Expr...on|array<integer,mixed> expected by parameter $predicates of TYPO3\CMS\Core\Database\...y\QueryBuilder::where(). ( Ignorable by Annotation )

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

167
                        /** @scrutinizer ignore-type */ $queryBuilder->expr()
Loading history...
168 1
                            ->eq(
169 1
                                'tablename',
170 1
                                $queryBuilder->createNamedParameter($tableName, PDO::PARAM_STR)
171
                            )
172
                    )
173 1
                    ->andWhere(
174 1
                        $queryBuilder->expr()
0 ignored issues
show
Bug introduced by
$queryBuilder->expr()->i...tion::PARAM_INT_ARRAY)) of type string is incompatible with the type Doctrine\DBAL\Query\Expr...n|array<integer,string> expected by parameter $where of TYPO3\CMS\Core\Database\...ueryBuilder::andWhere(). ( Ignorable by Annotation )

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

174
                        /** @scrutinizer ignore-type */ $queryBuilder->expr()
Loading history...
175 1
                            ->in(
176 1
                                'recuid',
177 1
                                $queryBuilder->createNamedParameter($recUidChunk, Connection::PARAM_INT_ARRAY)
178
                            )
179
                    );
180 1
                $queryBuilder->execute();
181
            }
182
        }
183 1
    }
184
185
    /**
186
     * @param string $tableName
187
     *
188
     * @return int[]
189
     */
190 1
    protected function getDeletableRecUidListFromTable($tableName): array
191
    {
192
        // Select all records from table, including deleted records
193 1
        $subQueryBuilder = $this->getQueryBuilderForTable($tableName);
194
        $subQueryBuilder
195 1
            ->select('uid')
196 1
            ->from($tableName);
197
198
        // Select all records from sys_refindex which are not in $tableName, including deleted records
199 1
        $queryBuilder = $this->getQueryBuilderForTable('sys_refindex');
200
        $queryBuilder
201 1
            ->select('recuid')
202 1
            ->from('sys_refindex')
203 1
            ->where(
204 1
                $queryBuilder->expr()
0 ignored issues
show
Bug introduced by
$queryBuilder->expr()->e...eName, PDO::PARAM_STR)) of type string is incompatible with the type Doctrine\DBAL\Query\Expr...on|array<integer,mixed> expected by parameter $predicates of TYPO3\CMS\Core\Database\...y\QueryBuilder::where(). ( Ignorable by Annotation )

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

204
                /** @scrutinizer ignore-type */ $queryBuilder->expr()
Loading history...
205 1
                    ->eq('tablename', $queryBuilder->createNamedParameter($tableName, PDO::PARAM_STR))
206
            )
207 1
            ->andWhere($queryBuilder->expr()->notIn('recuid', $subQueryBuilder->getSQL()))
0 ignored issues
show
Bug introduced by
$queryBuilder->expr()->n...QueryBuilder->getSQL()) of type string is incompatible with the type Doctrine\DBAL\Query\Expr...n|array<integer,string> expected by parameter $where of TYPO3\CMS\Core\Database\...ueryBuilder::andWhere(). ( Ignorable by Annotation )

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

207
            ->andWhere(/** @scrutinizer ignore-type */ $queryBuilder->expr()->notIn('recuid', $subQueryBuilder->getSQL()))
Loading history...
208 1
            ->groupBy('recuid');
209
210
        $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

210
        $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...
211 1
            ->execute()
212 1
            ->fetchAll(FetchMode::ASSOCIATIVE);
213
214 1
        $recUidList = [0];
215 1
        foreach ($allRecs as $recdat) {
216
            $recUidList[] = (int) $recdat['recuid'];
217
        }
218
219 1
        return $recUidList;
220
    }
221
222
    /**
223
     * @return ReferenceIndex
224
     */
225 1
    protected function getReferenceIndex(): ReferenceIndex
226
    {
227 1
        if ($this->referenceIndex === null) {
228 1
            $this->referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
229
        }
230
231 1
        return $this->referenceIndex;
232
    }
233
234
    /**
235
     * @param string $table
236
     * @param bool   $useEnableFields
237
     * @return QueryBuilder
238
     */
239 3
    private function getQueryBuilderForTable(string $table, bool $useEnableFields = false): QueryBuilder
240
    {
241 3
        $connectionPool = $this->getConnectionPool();
242 3
        $queryBuilder = $connectionPool->getQueryBuilderForTable($table);
243
244 3
        if ($useEnableFields === false) {
245 3
            $queryBuilder->getRestrictions()
246 3
                ->removeAll();
247
        }
248
249 3
        return $queryBuilder;
250
    }
251
252
    /**
253
     * @return ConnectionPool
254
     */
255 3
    private function getConnectionPool(): ConnectionPool
256
    {
257 3
        if ($this->connectionPool === null) {
258 3
            $this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
259
        }
260
261 3
        return $this->connectionPool;
262
    }
263
}
264