[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / DataHandling / PlainDataResolver.php
1 <?php
2 namespace TYPO3\CMS\Core\DataHandling;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Database\Connection;
19 use TYPO3\CMS\Core\Database\ConnectionPool;
20 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Core\Versioning\VersionState;
23
24 /**
25 * Plain data resolving.
26 *
27 * This component resolves data constraints for given IDs of a
28 * particular table on a plain/raw database level. Thus, workspaces
29 * placeholders and overlay related resorting is applied automatically.
30 */
31 class PlainDataResolver
32 {
33 /**
34 * @var string
35 */
36 protected $tableName;
37
38 /**
39 * @var int[]
40 */
41 protected $liveIds;
42
43 /**
44 * @var array
45 */
46 protected $sortingStatement;
47
48 /**
49 * @var int
50 */
51 protected $workspaceId;
52
53 /**
54 * @var bool
55 */
56 protected $keepLiveIds = false;
57
58 /**
59 * @var bool
60 */
61 protected $keepDeletePlaceholder = false;
62
63 /**
64 * @var bool
65 */
66 protected $keepMovePlaceholder = true;
67
68 /**
69 * @var int[]
70 */
71 protected $resolvedIds;
72
73 /**
74 * @param string $tableName
75 * @param int[] $liveIds
76 * @param null|array $sortingStatement
77 */
78 public function __construct($tableName, array $liveIds, array $sortingStatement = null)
79 {
80 $this->tableName = $tableName;
81 $this->liveIds = $this->reindex($liveIds);
82 $this->sortingStatement = $sortingStatement;
83 }
84
85 /**
86 * Sets the target workspace ID the final result shall use.
87 *
88 * @param int $workspaceId
89 */
90 public function setWorkspaceId($workspaceId)
91 {
92 $this->workspaceId = (int)$workspaceId;
93 }
94
95 /**
96 * Sets whether live IDs shall be kept in the final result set.
97 *
98 * @param bool $keepLiveIds
99 * @return PlainDataResolver
100 */
101 public function setKeepLiveIds($keepLiveIds)
102 {
103 $this->keepLiveIds = (bool)$keepLiveIds;
104 return $this;
105 }
106
107 /**
108 * Sets whether delete placeholders shall be kept in the final result set.
109 *
110 * @param bool $keepDeletePlaceholder
111 * @return PlainDataResolver
112 */
113 public function setKeepDeletePlaceholder($keepDeletePlaceholder)
114 {
115 $this->keepDeletePlaceholder = (bool)$keepDeletePlaceholder;
116 return $this;
117 }
118
119 /**
120 * Sets whether move placeholders shall be kept in case they cannot be substituted.
121 *
122 * @param bool $keepMovePlaceholder
123 * @return PlainDataResolver
124 */
125 public function setKeepMovePlaceholder($keepMovePlaceholder)
126 {
127 $this->keepMovePlaceholder = (bool)$keepMovePlaceholder;
128 return $this;
129 }
130
131 /**
132 * @return int[]
133 */
134 public function get()
135 {
136 if (isset($this->resolvedIds)) {
137 return $this->resolvedIds;
138 }
139
140 $this->resolvedIds = $this->processVersionOverlays($this->liveIds);
141 if ($this->resolvedIds !== $this->liveIds) {
142 $this->resolvedIds = $this->reindex($this->resolvedIds);
143 }
144
145 $tempIds = $this->processSorting($this->resolvedIds);
146 if ($tempIds !== $this->resolvedIds) {
147 $this->resolvedIds = $this->reindex($tempIds);
148 }
149
150 $tempIds = $this->applyLiveIds($this->resolvedIds);
151 if ($tempIds !== $this->resolvedIds) {
152 $this->resolvedIds = $this->reindex($tempIds);
153 }
154
155 return $this->resolvedIds;
156 }
157
158 /**
159 * Processes version overlays on the final result set.
160 *
161 * @param int[] $ids
162 * @return int[]
163 * @internal
164 */
165 public function processVersionOverlays(array $ids)
166 {
167 if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
168 return $ids;
169 }
170
171 $ids = $this->reindex(
172 $this->processVersionMovePlaceholders($ids)
173 );
174 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
175 ->getQueryBuilderForTable($this->tableName);
176
177 $queryBuilder->getRestrictions()->removeAll();
178
179 $result = $queryBuilder
180 ->select('uid', 't3ver_oid', 't3ver_state')
181 ->from($this->tableName)
182 ->where(
183 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
184 $queryBuilder->expr()->in(
185 't3ver_oid',
186 $queryBuilder->createNamedParameter($ids, Connection::PARAM_INT_ARRAY)
187 ),
188 $queryBuilder->expr()->eq(
189 't3ver_wsid',
190 $queryBuilder->createNamedParameter($this->workspaceId, \PDO::PARAM_INT)
191 )
192 )
193 ->execute();
194
195 while ($version = $result->fetch()) {
196 $liveReferenceId = $version['t3ver_oid'];
197 $versionId = $version['uid'];
198 if (isset($ids[$liveReferenceId])) {
199 if (!$this->keepDeletePlaceholder
200 && VersionState::cast($version['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)
201 ) {
202 unset($ids[$liveReferenceId]);
203 } else {
204 $ids[$liveReferenceId] = $versionId;
205 }
206 }
207 }
208
209 return $ids;
210 }
211
212 /**
213 * Processes and resolves move placeholders on the final result set.
214 *
215 * @param int[] $ids
216 * @return int[]
217 * @internal
218 */
219 public function processVersionMovePlaceholders(array $ids)
220 {
221 // Early return on insufficient data-set
222 if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
223 return $ids;
224 }
225
226 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
227 ->getQueryBuilderForTable($this->tableName);
228
229 $queryBuilder->getRestrictions()->removeAll();
230
231 $result = $queryBuilder
232 ->select('uid', 't3ver_move_id')
233 ->from($this->tableName)
234 ->where(
235 $queryBuilder->expr()->neq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
236 $queryBuilder->expr()->eq(
237 't3ver_state',
238 $queryBuilder->createNamedParameter((string)VersionState::MOVE_PLACEHOLDER, \PDO::PARAM_INT)
239 ),
240 $queryBuilder->expr()->eq(
241 't3ver_wsid',
242 $queryBuilder->createNamedParameter($this->workspaceId, \PDO::PARAM_INT)
243 ),
244 $queryBuilder->expr()->in(
245 't3ver_move_id',
246 $queryBuilder->createNamedParameter($ids, Connection::PARAM_INT_ARRAY)
247 )
248 )
249 ->execute();
250
251 while ($movePlaceholder = $result->fetch()) {
252 $liveReferenceId = $movePlaceholder['t3ver_move_id'];
253 $movePlaceholderId = $movePlaceholder['uid'];
254 // Substitute MOVE_PLACEHOLDER and purge live reference
255 if (isset($ids[$movePlaceholderId])) {
256 $ids[$movePlaceholderId] = $liveReferenceId;
257 unset($ids[$liveReferenceId]);
258 } elseif (!$this->keepMovePlaceholder) {
259 // Just purge live reference
260 unset($ids[$liveReferenceId]);
261 }
262 }
263
264 return $ids;
265 }
266
267 /**
268 * Processes sorting of the final result set, if
269 * a sorting statement (table column/expression) is given.
270 *
271 * @param int[] $ids
272 * @return int[]
273 * @internal
274 */
275 public function processSorting(array $ids)
276 {
277 // Early return on missing sorting statement or insufficient data-set
278 if (empty($this->sortingStatement) || count($ids) < 2) {
279 return $ids;
280 }
281
282 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
283 ->getQueryBuilderForTable($this->tableName);
284
285 $queryBuilder->getRestrictions()->removeAll();
286
287 $queryBuilder
288 ->select('uid')
289 ->from($this->tableName)
290 ->where(
291 $queryBuilder->expr()->in(
292 'uid',
293 $queryBuilder->createNamedParameter($ids, Connection::PARAM_INT_ARRAY)
294 )
295 );
296
297 if (!empty($this->sortingStatement)) {
298 foreach ($this->sortingStatement as $sortingStatement) {
299 $queryBuilder->add('orderBy', $sortingStatement, true);
300 }
301 }
302
303 $sortedIds = $queryBuilder->execute()->fetchAll();
304
305 return array_column($sortedIds, 'uid');
306 }
307
308 /**
309 * Applies live IDs to the final result set, if
310 * the current table is enabled for workspaces and
311 * the keepLiveIds class member is enabled.
312 *
313 * @param int[] $ids
314 * @return int[]
315 * @internal
316 */
317 public function applyLiveIds(array $ids)
318 {
319 if (!$this->keepLiveIds || !$this->isWorkspaceEnabled() || empty($ids)) {
320 return $ids;
321 }
322
323 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
324 ->getQueryBuilderForTable($this->tableName);
325
326 $queryBuilder->getRestrictions()->removeAll();
327
328 $result = $queryBuilder
329 ->select('uid', 't3ver_oid')
330 ->from($this->tableName)
331 ->where(
332 $queryBuilder->expr()->in(
333 'uid',
334 $queryBuilder->createNamedParameter($ids, Connection::PARAM_INT_ARRAY)
335 )
336 )
337 ->execute();
338
339 $versionIds = [];
340 while ($record = $result->fetch()) {
341 $liveId = $record['uid'];
342 $versionIds[$liveId] = $record['t3ver_oid'];
343 }
344
345 foreach ($ids as $id) {
346 if (!empty($versionIds[$id])) {
347 $ids[$id] = $versionIds[$id];
348 }
349 }
350
351 return $ids;
352 }
353
354 /**
355 * Re-indexes the given IDs.
356 *
357 * @param int[] $ids
358 * @return int[]
359 */
360 protected function reindex(array $ids)
361 {
362 if (empty($ids)) {
363 return $ids;
364 }
365 $ids = array_values($ids);
366 $ids = array_combine($ids, $ids);
367 return $ids;
368 }
369
370 /**
371 * @return bool
372 */
373 protected function isWorkspaceEnabled()
374 {
375 if (ExtensionManagementUtility::isLoaded('workspaces')) {
376 return BackendUtility::isTableWorkspaceEnabled($this->tableName);
377 }
378 return false;
379 }
380
381 /**
382 * @return bool
383 */
384 protected function isLocalizationEnabled()
385 {
386 return BackendUtility::isTableLocalizable($this->tableName);
387 }
388 }