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 ('' === $remote || '' === $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($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 (true === 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 (true === 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 (true === 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 null !== $resource ? $resource->getName() : null; } if (true === is_object($value)) { $this->logWarning('Form value skipped while collecting labeled values: unsupported type.', [ 'element' => $identifier, 'class' => get_class($value), ]); 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)); } }