Files
cms-core/Classes/DataHandling/Localization/DataMapItem.php
T

351 lines
10 KiB
PHP

<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\DataHandling\Localization;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
/**
* Entity for data-map item.
*
* @internal
*/
class DataMapItem
{
public const TYPE_PARENT = 'parent';
public const TYPE_DIRECT_CHILD = 'directChild';
public const TYPE_GRAND_CHILD = 'grandChild';
public const SCOPE_PARENT = State::STATE_PARENT;
public const SCOPE_SOURCE = State::STATE_SOURCE;
public const SCOPE_EXCLUDE = 'exclude';
protected string $tableName;
protected string|int $id;
protected int $liveId;
protected array $suggestedValues;
protected array $persistedValues;
protected array $configurationFieldNames;
protected bool $new;
protected ?string $type = null;
protected ?State $state = null;
protected string|int $language = 0;
protected string|int $parent = '';
protected string|int|null $source = null;
/** @var DataMapItem[][] */
protected array $dependencies = [];
/**
* Builds a data-map item. In addition to the constructor, the values
* for language, parent and source record pointers are assigned as well.
*/
public static function build(
string $tableName,
string|int $id,
array $suggestedValues,
array $persistedValues,
array $configurationFieldNames
): static {
$item = GeneralUtility::makeInstance(
static::class,
$tableName,
$id,
$suggestedValues,
$persistedValues,
$configurationFieldNames
);
// assign language specific settings of item
$item->language = (int)($suggestedValues[$item->getLanguageFieldName()] ?? $persistedValues[$item->getLanguageFieldName()] ?? 0);
$item->setParent($suggestedValues[$item->getParentFieldName()] ?? $persistedValues[$item->getParentFieldName()] ?? '');
if ($item->getSourceFieldName() !== null) {
$item->setSource($suggestedValues[$item->getSourceFieldName()] ?? $persistedValues[$item->getSourceFieldName()] ?? '');
}
// assign live-id of item if available
$versionSourceFieldName = $item->getVersionSourceFieldName();
if ($versionSourceFieldName !== null && !empty($persistedValues[$versionSourceFieldName])) {
$item->liveId = $persistedValues[$versionSourceFieldName];
}
return $item;
}
public function __construct(
string $tableName,
string|int $id,
array $suggestedValues,
array $persistedValues,
array $configurationFieldNames
) {
$this->tableName = $tableName;
$this->id = $id;
$this->suggestedValues = $suggestedValues;
$this->persistedValues = $persistedValues;
$this->configurationFieldNames = $configurationFieldNames;
$this->new = !MathUtility::canBeInterpretedAsInteger($id);
}
/**
* Gets the current table name of this data-map item.
*/
public function getTableName(): string
{
return $this->tableName;
}
/**
* Gets the id of this data-map item.
*/
public function getId(): string|int
{
return $this->id;
}
public function getLiveId(): int|string
{
return $this->liveId ?? $this->id;
}
/**
* Gets the suggested values that were initially
* submitted as the whole data-map to the DataHandler.
*/
public function getSuggestedValues(): array
{
return $this->suggestedValues;
}
/**
* Gets the persisted values that represent the persisted state
* of the record this data-map item is a surrogate for - does only
* contain relevant field values.
*/
public function getPersistedValues(): array
{
return $this->persistedValues;
}
public function getConfigurationFieldNames(): array
{
return $this->configurationFieldNames;
}
public function getLanguageFieldName(): string
{
return $this->configurationFieldNames['language'];
}
public function getParentFieldName(): string
{
return $this->configurationFieldNames['parent'];
}
public function getSourceFieldName(): ?string
{
return $this->configurationFieldNames['source'] ?? null;
}
protected function getVersionSourceFieldName(): ?string
{
return $this->configurationFieldNames['versionSource'] ?? null;
}
public function isNew(): bool
{
return $this->new;
}
public function getType(): string
{
if ($this->type === null) {
// implicit: default language, it's a parent
if ($this->language === 0) {
$this->type = static::TYPE_PARENT;
} elseif (
// implicit: having source value different to parent value, it's a 2nd or higher level translation
$this->source !== 0
&& $this->source !== null
&& $this->source !== $this->parent
) {
$this->type = static::TYPE_GRAND_CHILD;
} else {
// implicit: otherwise, it's a 1st level translation
$this->type = static::TYPE_DIRECT_CHILD;
}
}
return $this->type;
}
public function isParentType(): bool
{
return $this->getType() === static::TYPE_PARENT;
}
public function isDirectChildType(): bool
{
return $this->getType() === static::TYPE_DIRECT_CHILD;
}
public function isGrandChildType(): bool
{
return $this->getType() === static::TYPE_GRAND_CHILD;
}
public function getState(): ?State
{
if ($this->state === null && !$this->isParentType()) {
$this->state = $this->buildState();
}
return $this->state;
}
public function getLanguage(): string|int
{
return $this->language;
}
public function setLanguage(string|int $language): void
{
$this->language = $language;
}
public function getParent(): string|int
{
return $this->parent;
}
public function setParent(string|int $parent): void
{
$this->parent = $this->extractId($parent);
}
public function getSource(): string|int|null
{
return $this->source;
}
public function setSource(string|int $source): void
{
$this->source = $this->extractId($source);
}
public function getIdForScope(string $scope): string|int
{
if (
$scope === static::SCOPE_PARENT
|| $scope === static::SCOPE_EXCLUDE
) {
return $this->getParent();
}
if ($scope === static::SCOPE_SOURCE) {
// $source is guaranteed to be non-null when SCOPE_SOURCE is applicable:
// getApplicableScopes() only includes it when getSourceFieldName() !== null,
// which means setSource() was called during build().
return $this->source;
}
throw new \RuntimeException('Invalid scope', 1486325248);
}
/**
* @return DataMapItem[][]
*/
public function getDependencies(): array
{
return $this->dependencies;
}
/**
* @param DataMapItem[][] $dependencies
*/
public function setDependencies(array $dependencies): void
{
$this->dependencies = $dependencies;
}
/**
* @return DataMapItem[]
*/
public function findDependencies(string $scope): array
{
return $this->dependencies[$scope] ?? [];
}
/**
* @return string[]
*/
public function getApplicableScopes(): array
{
$scopes = [];
if (!empty($this->getSourceFieldName())) {
$scopes[] = static::SCOPE_SOURCE;
}
$scopes[] = static::SCOPE_PARENT;
$scopes[] = static::SCOPE_EXCLUDE;
return $scopes;
}
/**
* Extracts real id from provided id-value, which can either be a real
* integer value, a 'NEW...' id, or a combined identifier 'tt_content_13'.
*/
protected function extractId(int|string $idValue): int|string
{
if (MathUtility::canBeInterpretedAsInteger($idValue)) {
return $idValue;
}
$idValue = (string)$idValue;
if (str_starts_with($idValue, 'NEW')) {
return $idValue;
}
// @todo Handle if $tableName does not match $this->tableName
$id = BackendUtility::splitTable_Uid($idValue)[1];
return $id;
}
protected function buildState(): ?State
{
// build from persisted states
if (!$this->isNew()) {
$state = State::fromJSON(
$this->tableName,
$this->persistedValues['l10n_state'] ?? null
);
} elseif (is_string($this->suggestedValues['l10n_state'] ?? null)) {
// use provided states for a new and copied element
$state = State::fromJSON(
$this->tableName,
$this->suggestedValues['l10n_state']
);
} else {
// provide the default states
$state = State::create($this->tableName);
}
// switch "custom" to "source" state for 2nd level translations
if ($this->isNew() && $this->isGrandChildType()) {
$state->updateStates(State::STATE_CUSTOM, State::STATE_SOURCE);
}
// apply any provided updates to the states
if (is_array($this->suggestedValues['l10n_state'] ?? null)) {
$state->update($this->suggestedValues['l10n_state']);
}
return $state;
}
}