WoltLab Suite 6.2 Handbuch
Deutsch/PHP-Version: Die Website-Struktur, Navigation und Überschriften sind auf Deutsch vorbereitet. Code-Beispiele und technische Namen bleiben unverändert.

Form Validation and Form Data

Form Validation

Every form field class has to implement IFormField::validate() according to their internal logic of what constitutes a valid value.

If a certain constraint for the value is not met, a form field validation error object is added to the form field.

Form field validation error classes have to implement the interface IFormFieldValidationError.

In addition to intrinsic validations like checking the length of the value of a text form field, in many cases, there are additional constraints specific to the form like ensuring that the text is not already used by a different object of the same database object class.

Such additional validations can be added to (and removed from) the form field via implementations of the IFormFieldValidator interface.

IFormFieldValidationError / FormFieldValidationError

IFormFieldValidationError requires the following methods:

FormFieldValidationError is a default implementation of the interface that shows the error in an small.innerError HTML element below the form field.

Form field validation errors are added to form fields via the IFormField::addValidationError(IFormFieldValidationError $error) method.

IFormFieldValidator / FormFieldValidator

IFormFieldValidator requires the following methods:

FormFieldValidator is a default implementation of the interface.

Form field validators are added to form fields via the addValidator(IFormFieldValidator $validator) method.

Example

The following source code adds a validator that validates whether the value in the input field matches a specific value.

$container->appendChildren([
	FooField::create('a')
		->addValidator(new FormFieldValidator('b', function (FooField $formField) {
			if ($formField->getValue() != 'value') {
				$formField->addValidationError(
					new FormFieldValidationError(
						'type',
						'phrase'
					)
				);
			}
		})),
]);

Form Data

After a form is successfully validated, the data of the form fields (returned by IFormDocument::getData()) have to be extracted which is the job of the IFormDataHandler object returned by IFormDocument::getDataHandler().

Form data handlers themselves, however, are only iterating through all IFormDataProcessor instances that have been registered with the data handler.

IFormDataHandler / FormDataHandler

IFormDataHandler requires the following methods:

FormDataHandler is the default implementation of this interface and should also be extended instead of implementing the interface directly.

IFormDataProcessor / DefaultFormDataProcessor

IFormDataProcessor requires the following methods:

When FormDocument creates its FormDataHandler instance, it automatically registers an DefaultFormDataProcessor object as the first data processor.

DefaultFormDataProcessor puts the save value of all form fields that are available and have a save value into $parameters['data'] using the form field’s object property as the array key.

!!! warning "IFormDataProcessor should not be implemented directly. Instead, AbstractFormDataProcessor should be extended."

!!! info "All form data is put into the data sub-array so that the whole $parameters array can be passed to a database object action object that requires the actual database object data to be in the data sub-array."

!!! info "When adding a data processor to a form, make sure to add the data processor after the form has been built."

Additional Data Processors

CustomFormDataProcessor

As mentioned above, the data in the data sub-array is intended to directly create or update the database object with.

As these values are used in the database query directly, these values cannot contain arrays.

Several form fields, however, store and return their data in form of arrays.

Thus, this data cannot be returned by IFormField::getSaveValue() so that IFormField::hasSaveValue() returns false and the form field’s data is not collected by the standard DefaultFormDataProcessor object.

Instead, such form fields register a CustomFormDataProcessor in their IFormField::populate() method that inserts the form field value into the $parameters array directly.

This way, the relevant database object action method has access to the data to save it appropriately.

The constructor of CustomFormDataProcessor requires an id (that is primarily used in error messages during the validation of the second parameter) and callables for IFormDataProcessor::processFormData() and IFormDataProcessor::processObjectData() which are passed the same parameters as the IFormDataProcessor methods.

Only one of the callables has to be given, the other one then defaults to simply returning the relevant array unchanged.

Example

The following source code adds a custom processor that handles the return of MultilineItemListFormField and converts the content of an array into a multiline string in order to store it in the database.

$form->getDataHandler()->addProcessor(
    new CustomFormDataProcessor(
        'additionalItems',
        static function (IFormDocument $document, array $parameters) {
            $additionalItems = $document->getNodeById('additionalItems');
            \assert($additionalItems instanceof MultilineItemListFormField);

            $value = $additionalItems->getValue();
            if ($value === null || $value === []) {
                $parameters['data']['additionalItems'] = null;
            } else {
                $parameters['data']['additionalItems'] = \implode("\n", $value);
            }

            return $parameters;
        },
        static function (IFormDocument $document, array $data, IStorableObject $object) {
            if ($object->additionalItems !== null) {
                $data['additionalItems'] = \explode("\n", $data['additionalItems']);
            }

            return $data;
        }
    )
);

VoidFormDataProcessor

Some form fields might only exist to toggle the visibility of other form fields (via dependencies) but the data of form field itself is irrelevant.

As DefaultFormDataProcessor collects the data of all form fields, an additional data processor in the form of a VoidFormDataProcessor can be added whose constructor __construct($property, $isDataProperty = true) requires the name of the relevant object property/form id and whether the form field value is stored in the data sub-array or directory in the $parameters array.

When the data processor is invoked, it checks whether the relevant entry in the $parameters array exists and voids it by removing it from the array.