Completed
Push — master ( 76a80d...a62bd5 )
by
unknown
17:53
created

ReferenceIndexUpdater::update()   B

Complexity

Conditions 11
Paths 80

Size

Total Lines 65
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 40
nc 80
nop 0
dl 0
loc 65
rs 7.3166
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Core\DataHandling;
19
20
use TYPO3\CMS\Backend\Utility\BackendUtility;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\ReferenceIndex;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25
/**
26
 * Helper class for DataHandler to gather requests for reference index updates
27
 * and perform them in one go after other operations have been done.
28
 * This is used to suppress multiple reference index update calls for the same
29
 * workspace/table/uid combination within one DataHandler main call.
30
 *
31
 * @internal should only be used by the TYPO3 Core
32
 */
33
class ReferenceIndexUpdater
34
{
35
    /**
36
     * [ workspaceId => [ tableName => [ uid ] ] ]
37
     *
38
     * @var array<int, array<string, array<int, int>>>
39
     */
40
    protected $updateRegistry = [];
41
42
    /**
43
     * [ workspaceId => [ tableName => [
44
     *      'uid' => uid,
45
     *      'targetWorkspace' => $targetWorkspace
46
     * ] ] ]
47
     *
48
     * @var array<int, array<string, array<int, array<string, string|int>>>>
49
     */
50
    protected $updateRegistryToItem = [];
51
52
    /**
53
     * [ workspaceId => [ tableName => [ uid ] ] ]
54
     *
55
     * @var array<int, array<string, array<int, int>>>
56
     */
57
    protected $dropRegistry = [];
58
59
    /**
60
     * Register a workspace/table/uid row for update
61
     *
62
     * @param string $table Table name
63
     * @param int $uid Record uid
64
     * @param int $workspace Workspace the record lives in
65
     */
66
    public function registerForUpdate(string $table, int $uid, int $workspace): void
67
    {
68
        if ($workspace && !BackendUtility::isTableWorkspaceEnabled($table)) {
69
            // If a user is in some workspace and changes relations of not workspace aware
70
            // records, the reference index update needs to be performed as if the user
71
            // is in live workspace. This is detected here and the update is registered for live.
72
            $workspace = 0;
73
        }
74
        if (!isset($this->updateRegistry[$workspace][$table])) {
75
            $this->updateRegistry[$workspace][$table] = [];
76
        }
77
        if (!in_array($uid, $this->updateRegistry[$workspace][$table], true)) {
78
            $this->updateRegistry[$workspace][$table][] = $uid;
79
        }
80
    }
81
82
    /**
83
     * Find reference index rows pointing to given table/uid combination and register them for update. Important in
84
     * delete and publish scenarios where a child is deleted to make sure any references to this child are dropped, too.
85
     * In publish scenarios reference index may exist for a non live workspace, but should be updated for live workspace.
86
     * The optional $targetWorkspace argument is used for this.
87
     *
88
     * @param string $table Table name, used as ref_table
89
     * @param int $uid Record uid, used as ref_uid
90
     * @param int $workspace The workspace given record lives in
91
     * @param int $targetWorkspace The target workspace the record has been swapped to
92
     */
93
    public function registerUpdateForReferencesToItem(string $table, int $uid, int $workspace, int $targetWorkspace = null): void
94
    {
95
        if ($workspace && !BackendUtility::isTableWorkspaceEnabled($table)) {
96
            // If a user is in some workspace and changes relations of not workspace aware
97
            // records, the reference index update needs to be performed as if the user
98
            // is in live workspace. This is detected here and the update is registered for live.
99
            $workspace = 0;
100
        }
101
        if ($targetWorkspace === null) {
102
            $targetWorkspace = $workspace;
103
        }
104
        if (!isset($this->updateRegistryToItem[$workspace][$table])) {
105
            $this->updateRegistryToItem[$workspace][$table] = [];
106
        }
107
        $recordAndTargetWorkspace = [
108
            'uid' => $uid,
109
            'targetWorkspace' => $targetWorkspace
110
        ];
111
        if (!in_array($recordAndTargetWorkspace, $this->updateRegistryToItem[$workspace][$table], true)) {
112
            $this->updateRegistryToItem[$workspace][$table][] = $recordAndTargetWorkspace;
113
        }
114
    }
115
116
    /**
117
     * Delete rows from sys_refindex a table / uid combination is involved in:
118
     * Either on left side (tablename + recuid) OR right side (ref_table + ref_uid).
119
     * Useful in scenarios like workspace-discard where parents or children are hard deleted: The
120
     * expensive updateRefIndex() does not need to be called since we can just drop straight ahead.
121
     *
122
     * @param string $table Table name, used as tablename and ref_table
123
     * @param int $uid Record uid, used as recuid and ref_uid
124
     * @param int $workspace Workspace the record lives in
125
     */
126
    public function registerForDrop(string $table, int $uid, int $workspace): void
127
    {
128
        if ($workspace && !BackendUtility::isTableWorkspaceEnabled($table)) {
129
            // If a user is in some workspace and changes relations of not workspace aware
130
            // records, the reference index update needs to be performed as if the user
131
            // is in live workspace. This is detected here and the update is registered for live.
132
            $workspace = 0;
133
        }
134
        if (!isset($this->dropRegistry[$workspace][$table])) {
135
            $this->dropRegistry[$workspace][$table] = [];
136
        }
137
        if (!in_array($uid, $this->dropRegistry[$workspace][$table], true)) {
138
            $this->dropRegistry[$workspace][$table][] = $uid;
139
        }
140
    }
141
142
    /**
143
     * Perform the reference index update operations
144
     */
145
    public function update(): void
146
    {
147
        // Register updates to an item for update
148
        foreach ($this->updateRegistryToItem as $workspace => $tableArray) {
149
            foreach ($tableArray as $table => $recordArray) {
150
                foreach ($recordArray as $item) {
151
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
152
                    $statement = $queryBuilder
153
                        ->select('tablename', 'recuid')
154
                        ->from('sys_refindex')
155
                        ->where(
156
                            $queryBuilder->expr()->eq('ref_table', $queryBuilder->createNamedParameter($table)),
157
                            $queryBuilder->expr()->eq('ref_uid', $queryBuilder->createNamedParameter($item['uid'], \PDO::PARAM_INT)),
158
                            $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
159
                            $queryBuilder->expr()->eq('workspace', $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT))
160
                        )
161
                        ->execute();
162
                    while ($row = $statement->fetch()) {
163
                        $this->registerForUpdate($row['tablename'], (int)$row['recuid'], $item['targetWorkspace']);
0 ignored issues
show
Bug introduced by
It seems like $item['targetWorkspace'] can also be of type string; however, parameter $workspace of TYPO3\CMS\Core\DataHandl...er::registerForUpdate() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

163
                        $this->registerForUpdate($row['tablename'], (int)$row['recuid'], /** @scrutinizer ignore-type */ $item['targetWorkspace']);
Loading history...
164
                    }
165
                }
166
            }
167
        }
168
        $this->updateRegistryToItem = [];
169
170
        // Drop rows from reference index if requested. Note this is performed *after* update-to-item, to
171
        // find rows pointing to a record and register updates before rows are dropped. Needed if a record
172
        // changes the workspace during publish: In this case all records pointing to the record in a workspace
173
        // need to be registered for update for live workspace and after that the workspace rows can be dropped.
174
        foreach ($this->dropRegistry as $workspace => $tableArray) {
175
            foreach ($tableArray as $table => $uidArray) {
176
                foreach ($uidArray as $uid) {
177
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
178
                    $queryBuilder->delete('sys_refindex')
179
                        ->where(
180
                            $queryBuilder->expr()->eq('workspace', $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)),
181
                            $queryBuilder->expr()->orX(
182
                                $queryBuilder->expr()->andX(
183
                                    $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table)),
184
                                    $queryBuilder->expr()->eq('recuid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))
185
                                ),
186
                                $queryBuilder->expr()->andX(
187
                                    $queryBuilder->expr()->eq('ref_table', $queryBuilder->createNamedParameter($table)),
188
                                    $queryBuilder->expr()->eq('ref_uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))
189
                                )
190
                            )
191
                        )
192
                        ->execute();
193
                }
194
            }
195
        }
196
        $this->dropRegistry = [];
197
198
        // Perform reference index updates
199
        $referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
200
        $referenceIndex->enableRuntimeCache();
201
        foreach ($this->updateRegistry as $workspace => $tableArray) {
202
            $referenceIndex->setWorkspaceId($workspace);
203
            foreach ($tableArray as $table => $uidArray) {
204
                foreach ($uidArray as $uid) {
205
                    $referenceIndex->updateRefIndexTable($table, $uid);
206
                }
207
            }
208
        }
209
        $this->updateRegistry = [];
210
    }
211
}
212