rector.php gets the deadCode set: @param/@return/@var tags that only repeat native types, === true comparisons, three unused constructor dependencies (DateService, ProductDataService, AjaxWatchlistController), the unused reseller lookup in ContextProcessor and an always-false check in AbstractMappedFinisher (trimExplode with removeEmpty never yields an empty remote name). Checked locally: frontend crawl (~1,900 URLs) and the backchannel APIs render the same as before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
157 lines
5.1 KiB
PHP
157 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace EP\EpTheme\Form;
|
|
|
|
use TYPO3\CMS\Core\Log\LogManager;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
|
|
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
|
|
|
|
abstract class AbstractMappedFinisher extends AbstractFinisher
|
|
{
|
|
/**
|
|
* Resolves the 'mapping' option into remote name => local form element identifier.
|
|
*/
|
|
protected function parseMapping(): array
|
|
{
|
|
$mappings = [];
|
|
$mappingOption = (string)$this->parseOption('mapping');
|
|
|
|
foreach (GeneralUtility::trimExplode(',', $mappingOption, true) as $mapping) {
|
|
$parts = GeneralUtility::trimExplode(':', $mapping, true, 2);
|
|
if (2 !== count($parts)) {
|
|
continue;
|
|
}
|
|
|
|
[$remote, $local] = $parts;
|
|
if ('' === $local) {
|
|
continue;
|
|
}
|
|
|
|
$mappings[$remote] = $local;
|
|
}
|
|
|
|
return $mappings;
|
|
}
|
|
|
|
/**
|
|
* The mapped values, keyed by the remote name the receiving side expects. Values pass through
|
|
* normalizeValue() just like the labeled ones do, so a date element arrives as a Y-m-d string
|
|
* instead of a serialized DateTime. A value that does not survive normalization keeps its key:
|
|
* the remote names make up the request shape and dropping one would change it.
|
|
*/
|
|
protected function buildPayload(): array
|
|
{
|
|
$values = [];
|
|
$formValues = $this->finisherContext->getFormValues();
|
|
|
|
foreach ($this->parseMapping() as $remote => $local) {
|
|
if (false === array_key_exists((string) $local, $formValues)) {
|
|
continue;
|
|
}
|
|
|
|
$values[$remote] = $this->normalizeValue($formValues[$local], $local);
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
/**
|
|
* Every submitted value that the mapping does not already cover, keyed by the element label
|
|
* as the editor sees it in the form editor. Forms built in the backend use generated
|
|
* identifiers (text-1, singleselect-2, ...) that carry no meaning and cannot be renamed
|
|
* without breaking existing finisher options, so the label is the only stable description
|
|
* of what was asked.
|
|
*
|
|
* The label is taken from the form definition, not through TranslationService: a translated
|
|
* label would change the payload keys with the site language, and these keys are stored.
|
|
*/
|
|
protected function buildLabeledValues(): array
|
|
{
|
|
$formValues = $this->finisherContext->getFormValues();
|
|
$skip = array_merge(
|
|
array_values($this->parseMapping()),
|
|
GeneralUtility::trimExplode(',', (string)$this->parseOption('exclude'), true)
|
|
);
|
|
|
|
// Keyed by identifier in form order, and contains only real form elements - iterating
|
|
// this instead of the form values keeps the collection in the order of the form and
|
|
// leaves out anything that is not an element the visitor filled in.
|
|
$elements = $this->finisherContext->getFormRuntime()->getFormDefinition()->getElements();
|
|
|
|
$collected = [];
|
|
foreach ($elements as $identifier => $element) {
|
|
if (in_array($identifier, $skip, true) || false === array_key_exists($identifier, $formValues)) {
|
|
continue;
|
|
}
|
|
|
|
$value = $this->normalizeValue($formValues[$identifier], $identifier);
|
|
if (null === $value || '' === $value || [] === $value) {
|
|
continue;
|
|
}
|
|
|
|
$label = trim($element->getLabel());
|
|
if ('' === $label) {
|
|
$label = $identifier;
|
|
}
|
|
if (array_key_exists($label, $collected)) {
|
|
$label .= ' (' . $identifier . ')';
|
|
}
|
|
|
|
$collected[$label] = $value;
|
|
}
|
|
|
|
return $collected;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $value
|
|
*
|
|
* @return mixed null when the value has no JSON representation
|
|
*/
|
|
protected function normalizeValue($value, string $identifier)
|
|
{
|
|
if (is_array($value)) {
|
|
$normalized = [];
|
|
foreach ($value as $item) {
|
|
$item = $this->normalizeValue($item, $identifier);
|
|
if (null !== $item && '' !== $item) {
|
|
$normalized[] = $item;
|
|
}
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
if ($value instanceof \DateTimeInterface) {
|
|
return $value->format('Y-m-d');
|
|
}
|
|
|
|
if ($value instanceof FileReference) {
|
|
$resource = $value->getOriginalResource();
|
|
|
|
return $resource?->getName();
|
|
}
|
|
|
|
if (is_object($value)) {
|
|
$this->logWarning('Form value skipped while collecting labeled values: unsupported type.', [
|
|
'element' => $identifier,
|
|
'class' => $value::class,
|
|
]);
|
|
|
|
return null;
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
protected function logWarning(string $message, array $context = []): void
|
|
{
|
|
GeneralUtility::makeInstance(LogManager::class)
|
|
->getLogger(static::class)
|
|
->warning($message, array_merge([
|
|
'finisher' => static::class,
|
|
], $context));
|
|
}
|
|
}
|