6e214334a7305ea06a50b89a16d75354e79ae9a1
[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\Versioning\VersionState;
19
20 /**
21 * Plain data resolving.
22 *
23 * This component resolves data constraints for given IDs of a
24 * particular table on a plain/raw database level. Thus, workspaces
25 * placeholders and overlay related resorting is applied automatically.
26 */
27 class PlainDataResolver
28 {
29 /**
30 * @var string
31 */
32 protected $tableName;
33
34 /**
35 * @var int[]
36 */
37 protected $liveIds;
38
39 /**
40 * @var string
41 */
42 protected $sortingStatement;
43
44 /**
45 * @var int
46 */
47 protected $workspaceId;
48
49 /**
50 * @var bool
51 */
52 protected $keepLiveIds = false;
53
54 /**
55 * @var bool
56 */
57 protected $keepDeletePlaceholder = false;
58
59 /**
60 * @var bool
61 */
62 protected $keepMovePlaceholder = true;
63
64 /**
65 * @var int[]
66 */
67 protected $resolvedIds;
68
69 /**
70 * @param string $tableName
71 * @param int[] $liveIds
72 * @param NULL|string $sortingStatement
73 */
74 public function __construct($tableName, array $liveIds, $sortingStatement = null)
75 {
76 $this->tableName = $tableName;
77 $this->liveIds = $this->reindex($liveIds);
78 $this->sortingStatement = $sortingStatement;
79 }
80
81 /**
82 * Sets the target workspace ID the final result shall use.
83 *
84 * @param int $workspaceId
85 */
86 public function setWorkspaceId($workspaceId)
87 {
88 $this->workspaceId = (int)$workspaceId;
89 }
90
91 /**
92 * Sets whether live IDs shall be kept in the final result set.
93 *
94 * @param bool $keepLiveIds
95 * @return PlainDataResolver
96 */
97 public function setKeepLiveIds($keepLiveIds)
98 {
99 $this->keepLiveIds = (bool)$keepLiveIds;
100 return $this;
101 }
102
103 /**
104 * Sets whether delete placeholders shall be kept in the final result set.
105 *
106 * @param bool $keepDeletePlaceholder
107 * @return PlainDataResolver
108 */
109 public function setKeepDeletePlaceholder($keepDeletePlaceholder)
110 {
111 $this->keepDeletePlaceholder = (bool)$keepDeletePlaceholder;
112 return $this;
113 }
114
115 /**
116 * Sets whether move placeholders shall be kept in case they cannot be substituted.
117 *
118 * @param bool $keepMovePlaceholder
119 * @return PlainDataResolver
120 */
121 public function setKeepMovePlaceholder($keepMovePlaceholder)
122 {
123 $this->keepMovePlaceholder = (bool)$keepMovePlaceholder;
124 return $this;
125 }
126
127 /**
128 * @return int[]
129 */
130 public function get()
131 {
132 if (isset($this->resolvedIds)) {
133 return $this->resolvedIds;
134 }
135
136 $ids = $this->reindex(
137 $this->processVersionOverlays($this->liveIds)
138 );
139 $ids = $this->reindex(
140 $this->processSorting($ids)
141 );
142 $ids = $this->reindex(
143 $this->applyLiveIds($ids)
144 );
145
146 $this->resolvedIds = $ids;
147 return $this->resolvedIds;
148 }
149
150 /**
151 * Processes version overlays on the final result set.
152 *
153 * @param int[] $ids
154 * @return int[]
155 * @internal
156 */
157 public function processVersionOverlays(array $ids)
158 {
159 if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
160 return $ids;
161 }
162
163 $ids = $this->reindex(
164 $this->processVersionMovePlaceholders($ids)
165 );
166 $versions = $this->getDatabaseConnection()->exec_SELECTgetRows(
167 'uid,t3ver_oid,t3ver_state',
168 $this->tableName,
169 'pid=-1 AND t3ver_oid IN (' . $this->intImplode(',', $ids) . ')'
170 . ' AND t3ver_wsid=' . $this->workspaceId
171 );
172
173 if (!empty($versions)) {
174 foreach ($versions as $version) {
175 $liveReferenceId = $version['t3ver_oid'];
176 $versionId = $version['uid'];
177 if (isset($ids[$liveReferenceId])) {
178 if (!$this->keepDeletePlaceholder && VersionState::cast($version['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
179 unset($ids[$liveReferenceId]);
180 } else {
181 $ids[$liveReferenceId] = $versionId;
182 }
183 }
184 }
185 }
186
187 return $ids;
188 }
189
190 /**
191 * Processes and resolves move placeholders on the final result set.
192 *
193 * @param int[] $ids
194 * @return int[]
195 * @internal
196 */
197 public function processVersionMovePlaceholders(array $ids)
198 {
199 // Early return on insufficient data-set
200 if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
201 return $ids;
202 }
203
204 $movePlaceholders = $this->getDatabaseConnection()->exec_SELECTgetRows(
205 'uid,t3ver_move_id',
206 $this->tableName,
207 'pid<>-1 AND t3ver_state=' . VersionState::MOVE_PLACEHOLDER
208 . ' AND t3ver_wsid=' . $this->workspaceId
209 . ' AND t3ver_move_id IN (' . $this->intImplode(',', $ids) . ')'
210 );
211
212 if (!empty($movePlaceholders)) {
213 foreach ($movePlaceholders as $movePlaceholder) {
214 $liveReferenceId = $movePlaceholder['t3ver_move_id'];
215 $movePlaceholderId = $movePlaceholder['uid'];
216 // Substitute MOVE_PLACEHOLDER and purge live reference
217 if (isset($ids[$movePlaceholderId])) {
218 $ids[$movePlaceholderId] = $liveReferenceId;
219 unset($ids[$liveReferenceId]);
220 // Just purge live reference
221 } elseif (!$this->keepMovePlaceholder) {
222 unset($ids[$liveReferenceId]);
223 }
224 }
225 }
226
227 return $ids;
228 }
229
230 /**
231 * Processes sorting of the final result set, if
232 * a sorting statement (table column/expression) is given.
233 *
234 * @param int[] $ids
235 * @return int[]
236 * @internal
237 */
238 public function processSorting(array $ids)
239 {
240 // Early return on missing sorting statement or insufficient data-set
241 if (empty($this->sortingStatement) || count($ids) < 2) {
242 return $ids;
243 }
244
245 $records = $this->getDatabaseConnection()->exec_SELECTgetRows(
246 'uid',
247 $this->tableName,
248 'uid IN (' . $this->intImplode(',', $ids) . ')',
249 '',
250 $this->sortingStatement,
251 '',
252 'uid'
253 );
254
255 if (!is_array($records)) {
256 return [];
257 }
258
259 $ids = array_keys($records);
260 return $ids;
261 }
262
263 /**
264 * Applies live IDs to the final result set, if
265 * the current table is enabled for workspaces and
266 * the keepLiveIds class member is enabled.
267 *
268 * @param int[] $ids
269 * @return int[]
270 * @internal
271 */
272 public function applyLiveIds(array $ids)
273 {
274 if (!$this->keepLiveIds || !$this->isWorkspaceEnabled() || empty($ids)) {
275 return $ids;
276 }
277
278 $records = $this->getDatabaseConnection()->exec_SELECTgetRows(
279 'uid,t3ver_oid',
280 $this->tableName,
281 'uid IN (' . $this->intImplode(',', $ids) . ')',
282 '',
283 '',
284 '',
285 'uid'
286 );
287
288 if (!is_array($records)) {
289 return [];
290 }
291
292 foreach ($ids as $id) {
293 if (!empty($records[$id]['t3ver_oid'])) {
294 $ids[$id] = $records[$id]['t3ver_oid'];
295 }
296 }
297
298 return $ids;
299 }
300
301 /**
302 * Re-indexes the given IDs.
303 *
304 * @param int[] $ids
305 * @return int[]
306 */
307 protected function reindex(array $ids)
308 {
309 if (empty($ids)) {
310 return $ids;
311 }
312 $ids = array_values($ids);
313 $ids = array_combine($ids, $ids);
314 return $ids;
315 }
316
317 /**
318 * @return bool
319 */
320 protected function isWorkspaceEnabled()
321 {
322 return BackendUtility::isTableWorkspaceEnabled($this->tableName);
323 }
324
325 /**
326 * @return bool
327 */
328 protected function isLocalizationEnabled()
329 {
330 return BackendUtility::isTableLocalizable($this->tableName);
331 }
332
333 /**
334 * Implodes an array of casted integer values.
335 *
336 * @param string $delimiter
337 * @param array $values
338 * @return string
339 */
340 protected function intImplode($delimiter, array $values)
341 {
342 return implode($delimiter, $this->getDatabaseConnection()->cleanIntArray($values));
343 }
344
345 /**
346 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
347 */
348 protected function getDatabaseConnection()
349 {
350 return $GLOBALS['TYPO3_DB'];
351 }
352 }