91f930537f772c673cbc2d055a0cd70f6d7ec473
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / DataHandling / Localization / DataMapItem.php
1 <?php
2 namespace TYPO3\CMS\Core\DataHandling\Localization;
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\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Core\Utility\MathUtility;
19
20 /**
21 * Entity for data-map item.
22 */
23 class DataMapItem
24 {
25 const TYPE_PARENT = 'parent';
26 const TYPE_DIRECT_CHILD = 'directChild';
27 const TYPE_GRAND_CHILD = 'grandChild';
28
29 const SCOPE_PARENT = State::STATE_PARENT;
30 const SCOPE_SOURCE = State::STATE_SOURCE;
31 const SCOPE_EXCLUDE = 'exclude';
32
33 /**
34 * @var string
35 */
36 protected $tableName;
37
38 /**
39 * @var string|int
40 */
41 protected $id;
42
43 /**
44 * @var array
45 */
46 protected $suggestedValues;
47
48 /**
49 * @var array
50 */
51 protected $persistedValues;
52
53 /**
54 * @var array
55 */
56 protected $configurationFieldNames;
57
58 /**
59 * @var bool
60 */
61 protected $new;
62
63 /**
64 * @var string
65 */
66 protected $type;
67
68 /**
69 * @var State
70 */
71 protected $state;
72
73 /**
74 * @var string|int
75 */
76 protected $language;
77
78 /**
79 * @var string|int
80 */
81 protected $parent;
82
83 /**
84 * @var string|int
85 */
86 protected $source;
87
88 /**
89 * @var DataMapItem[][]
90 */
91 protected $dependencies = [];
92
93 /**
94 * Builds a data-map item. In addition to the constructor, the values
95 * for language, parent and source record pointers are assigned as well.
96 *
97 * @param string $tableName
98 * @param string|int $id
99 * @param array $suggestedValues
100 * @param array $persistedValues
101 * @param array $configurationFieldNames
102 * @return object|DataMapItem
103 */
104 public static function build(
105 string $tableName,
106 $id,
107 array $suggestedValues,
108 array $persistedValues,
109 array $configurationFieldNames
110 ) {
111 $item = GeneralUtility::makeInstance(
112 static::class,
113 $tableName,
114 $id,
115 $suggestedValues,
116 $persistedValues,
117 $configurationFieldNames
118 );
119
120 $item->language = (int)($suggestedValues[$item->getLanguageFieldName()] ?? $persistedValues[$item->getLanguageFieldName()]);
121 $item->setParent($suggestedValues[$item->getParentFieldName()] ?? $persistedValues[$item->getParentFieldName()]);
122 if ($item->getSourceFieldName() !== null) {
123 $item->setSource($suggestedValues[$item->getSourceFieldName()] ?? $persistedValues[$item->getSourceFieldName()]);
124 }
125
126 return $item;
127 }
128
129 /**
130 * @param string $tableName
131 * @param string|int $id
132 * @param array $suggestedValues
133 * @param array $persistedValues
134 * @param array $configurationFieldNames
135 */
136 public function __construct(
137 string $tableName,
138 $id,
139 array $suggestedValues,
140 array $persistedValues,
141 array $configurationFieldNames
142 ) {
143 $this->tableName = $tableName;
144 $this->id = $id;
145
146 $this->suggestedValues = $suggestedValues;
147 $this->persistedValues = $persistedValues;
148 $this->configurationFieldNames = $configurationFieldNames;
149
150 $this->new = !MathUtility::canBeInterpretedAsInteger($id);
151 }
152
153 /**
154 * Gets the current table name of this data-map item.
155 *
156 * @return string
157 */
158 public function getTableName(): string
159 {
160 return $this->tableName;
161 }
162
163 /**
164 * Gets the table name used to resolve the language parent record.
165 *
166 * @return string
167 */
168 public function getFromTableName(): string
169 {
170 if ($this->tableName === 'pages_language_overlay') {
171 return 'pages';
172 }
173 return $this->tableName;
174 }
175
176 /**
177 * Gets the table name used to resolve any kind of translations.
178 *
179 * @return string
180 */
181 public function getForTableName(): string
182 {
183 if ($this->tableName === 'pages') {
184 return 'pages_language_overlay';
185 }
186 return $this->tableName;
187 }
188
189 /**
190 * Gets the id of this data-map item.
191 *
192 * @return mixed
193 */
194 public function getId()
195 {
196 return $this->id;
197 }
198
199 /**
200 * Gets the suggested values that were initially
201 * submitted as the whole data-map to the DataHandler.
202 *
203 * @return array
204 */
205 public function getSuggestedValues(): array
206 {
207 return $this->suggestedValues;
208 }
209
210 /**
211 * Gets the persisted values that represent the persisted state
212 * of the record this data-map item is a surrogate for - does only
213 * contain relevant field values.
214 *
215 * @return array
216 */
217 public function getPersistedValues(): array
218 {
219 return $this->persistedValues;
220 }
221
222 /**
223 * @return array
224 */
225 public function getConfigurationFieldNames(): array
226 {
227 return $this->configurationFieldNames;
228 }
229
230 /**
231 * @return string
232 */
233 public function getLanguageFieldName(): string
234 {
235 return $this->configurationFieldNames['language'];
236 }
237
238 /**
239 * @return string
240 */
241 public function getParentFieldName(): string
242 {
243 return $this->configurationFieldNames['parent'];
244 }
245
246 /**
247 * @return null|string
248 */
249 public function getSourceFieldName()
250 {
251 return $this->configurationFieldNames['source'];
252 }
253
254 /**
255 * @return bool
256 */
257 public function isNew(): bool
258 {
259 return $this->new;
260 }
261
262 /**
263 * @return string
264 */
265 public function getType(): string
266 {
267 if ($this->type === null) {
268 // implicit: default language, it's a parent
269 if ($this->language === 0) {
270 $this->type = static::TYPE_PARENT;
271 // implicit: having source value different to parent value, it's a 2nd or higher level translation
272 } elseif (
273 $this->source !== null
274 && $this->source !== $this->parent
275 ) {
276 $this->type = static::TYPE_GRAND_CHILD;
277 // implicit: otherwise, it's a 1st level translation
278 } else {
279 $this->type = static::TYPE_DIRECT_CHILD;
280 }
281 }
282 return $this->type;
283 }
284
285 /**
286 * @return bool
287 */
288 public function isParentType(): bool
289 {
290 return $this->getType() === static::TYPE_PARENT;
291 }
292
293 /**
294 * @return bool
295 */
296 public function isDirectChildType(): bool
297 {
298 return $this->getType() === static::TYPE_DIRECT_CHILD;
299 }
300
301 /**
302 * @return bool
303 */
304 public function isGrandChildType(): bool
305 {
306 return $this->getType() === static::TYPE_GRAND_CHILD;
307 }
308
309 /**
310 * @return State
311 */
312 public function getState(): State
313 {
314 if ($this->state === null && !$this->isParentType()) {
315 $this->state = $this->buildState();
316 }
317 return $this->state;
318 }
319
320 /**
321 * @return string|int
322 */
323 public function getLanguage()
324 {
325 return $this->language;
326 }
327
328 /**
329 * @param string|int $language
330 */
331 public function setLanguage($language)
332 {
333 $this->language = $language;
334 }
335
336 /**
337 * @return string|int
338 */
339 public function getParent()
340 {
341 return $this->parent;
342 }
343
344 /**
345 * @param string|int $parent
346 */
347 public function setParent($parent)
348 {
349 $this->parent = $parent;
350 }
351
352 /**
353 * @return string|int
354 */
355 public function getSource()
356 {
357 return $this->source;
358 }
359
360 /**
361 * @param string|int $source
362 */
363 public function setSource($source)
364 {
365 $this->source = $source;
366 }
367
368 /**
369 * @param string $scope
370 * @return int|string
371 */
372 public function getIdForScope($scope)
373 {
374 if (
375 $scope === static::SCOPE_PARENT
376 || $scope === static::SCOPE_EXCLUDE
377 ) {
378 return $this->getParent();
379 }
380 if ($scope === static::SCOPE_SOURCE) {
381 return $this->getSource();
382 }
383 throw new \RuntimeException('Invalid scope', 1486325248);
384 }
385
386 /**
387 * @return DataMapItem[][]
388 */
389 public function getDependencies(): array
390 {
391 return $this->dependencies;
392 }
393
394 /**
395 * @param DataMapItem[][] $dependencies
396 */
397 public function setDependencies(array $dependencies)
398 {
399 $this->dependencies = $dependencies;
400 }
401
402 /**
403 * @param string $scope
404 * @return DataMapItem[]
405 */
406 public function findDependencies(string $scope)
407 {
408 return $this->dependencies[$scope] ?? [];
409 }
410
411 /**
412 * @return string[]
413 */
414 public function getApplicableScopes()
415 {
416 $scopes = [];
417 if (!empty($this->getSourceFieldName())) {
418 $scopes[] = static::SCOPE_SOURCE;
419 }
420 $scopes[] = static::SCOPE_PARENT;
421 $scopes[] = static::SCOPE_EXCLUDE;
422 return $scopes;
423 }
424
425 /**
426 * @return null|State
427 */
428 protected function buildState()
429 {
430 // build from persisted states
431 if (!$this->isNew()) {
432 $state = State::fromJSON(
433 $this->tableName,
434 $this->persistedValues['l10n_state'] ?? null
435 );
436 // use provided states for a new and copied element
437 } elseif (is_string($this->suggestedValues['l10n_state'] ?? null)) {
438 $state = State::fromJSON(
439 $this->tableName,
440 $this->suggestedValues['l10n_state']
441 );
442 // provide the default states
443 } else {
444 $state = State::create($this->tableName);
445 }
446 // switch "custom" to "source" state for 2nd level translations
447 if ($this->isNew() && $this->isGrandChildType()) {
448 $state->updateStates(State::STATE_CUSTOM, State::STATE_SOURCE);
449 }
450 // apply any provided updates to the states
451 if (is_array($this->suggestedValues['l10n_state'] ?? null)) {
452 $state->update(
453 $this->suggestedValues['l10n_state'] ?? []
454 );
455 }
456 return $state;
457 }
458 }