Project

General

Profile

Actions

Bug #101438

open

Having a validator in formElementsDefinition.Element and subsequent in its formEditor.predefinedDefaults.validators executes it twice with different options

Added by Stefan P 9 months ago. Updated 9 months ago.

Status:
New
Priority:
Should have
Assignee:
-
Category:
Form Framework
Target version:
-
Start date:
2023-07-25
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
12
PHP Version:
Tags:
Complexity:
Is Regression:
Sprint Focus:

Description

I wrote a custom form element that should force some validation that is neither removable in the Backend editor nor in the YAML file, but provides some options.

So I added

formElementsDefinition:
    MyElement:
        validators:
            # force the validation in the runtime, make it non-removable even via concrete form YAML
            - identifier: MyValidator
        formEditor:
            predefinedDefaults:
                validators:
                    # force the rendering, make it non-removable in the editor as well, so that editor always stays in control over the options
                    - identifier: MyValidator

The predefined defaults must be there so that the validator is rendered into the form editor (it must be rendered there because it has options to be set by the backend users). And the element validators section is there so that the validator is always foreced and can not be removed by the editing the form YAML file.

BUG: But this causes the validator to be instantiated and executed twice. Once with default options and once with the options entered in the form editor.

For now I have removed the form defintion validator, leaving only

formElementsDefinition:
    MyElement:
        formEditor:
            predefinedDefaults:
                validators:
                    - identifier: MyValidator

because this always renders the validator into the form editor and saves it into the YAML file.

But now it is possible to edit the concrete form YAML file and just remove the validator there. Then it is not executed at all (because the element defintion does not have it anymore)! But I need to force it under all costs.

I think this is an architectural bug. Validators with the same identifier should be "singleton" to a unique form field, using the option values as entered in the editor (form YAML) with fallback to the defaults in the defintion! That's what I totally intuitively expected when I wrote my custom field - but it turned out to be not the case.

Actions #1

Updated by Stefan P 9 months ago

I currently work around this by overriding the addValidator method in my FormElement implementation class. It checks if MyValidator was already added and updates its options (this relies on the fact that currently ext:form does add the validator declarations in the correct order, at least). If it was not added already it just adds it by using the original method.

final class MyElement extends \TYPO3\CMS\Form\Domain\Model\FormElements\AbstractFormElement
{
    public function addValidator(ValidatorInterface $validator): void
    {
        $existingMyValidator = null;
        foreach ($this->getRootForm()->getProcessingRule($this->getIdentifier())->getValidators() as $alreadyAddedValidator) {
            if ($alreadyAddedValidator instanceof MyValidator) {
                $existingMyValidator = $alreadyAddedValidator;
                break;
            }
        }

        if (null === $existingMyValidator) {
            parent::addValidator($validator);
        } else {
            $existingMyValidator->setOptions(\array_merge($existingMyValidator->getOptions(), $validator->getOptions()));
        }
    }
}

I think some similar logic should be in the AbstractFormElement::addValidator method, so that each validator type is only present once for a unique form field with the correct options (options fallback chain: concrete form YAML -> validator definition YAML -> validator PHP class defaults).

Actions

Also available in: Atom PDF