---
title: "Form Normalizer"
manual: "mosparo integration for EXT:form and Extbase-based forms"
version: "1.1"
permalink: "https://docs.typo3.org/permalink/denkwerk/mosparo-form:form-normalizer@1.1"
source: "Configuration/Custom-form-normalizer.rst"
rendered: "2026-11-11T13:09:55+00:00"
---

# Form Normalizer {#form-normalizer}

In order to verify form submissions correctly with mosparo, the form data must be structured
in a specific way. This includes:

-   The field names (keys) must exactly match the `name` attributes used in the frontend form,
    e.g. `tx_contact_contact_form[contact][topic]`
-   All visible and verifiable fields must be included to allow mosparo to confirm the integrity
    of the submission

Since different extensions or custom form setups store form data differently,
a dedicated [form normalizer](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-form-normalizer-interface@1.1) must be implemented to extract and prepare this data.

When the [MosparoCaptchaValidator](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-mosparo-captcha-validator@1.1) is executed,
it queries all registered normalizers (tagged as `mosparo.form_normalizer`) in the order of registration
and uses the **first** normalizer whose `supports(...)` method returns `true`.

That normalizer is then responsible for transforming the raw `$_POST` data into a structured
[NormalizedData](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-normalized-data@1.1) object. This includes assigning the verifiable and required fields,
which mosparo uses to verify whether the submitted data has been tampered with or is valid.

> [!NOTE]
> **See also**
>
> For more details, see the mosparo integration documentation:\
> [Performing verification](https://documentation.mosparo.io/docs/integration/custom#performing-verification)\
> [Prepare the form data](https://documentation.mosparo.io/docs/integration/custom#3-prepare-the-form-data)\
> [Ignored fields](https://documentation.mosparo.io/docs/integration/ignored_fields)\
> [Bypass protection](https://documentation.mosparo.io/docs/integration/bypass_protection)

> [!NOTE]
> **See also**
>
> For the full implementation, see the source code of the [mosparo Powermail](https://extensions.typo3.org/extension/mosparo_powermail) integration on GitHub: [https://github.com/Digi92/mosparo-powermail](https://github.com/Digi92/mosparo-powermail)

## Create a custom form normalizer {#form-normalizer-custom}

This section explains how to create and register a custom form normalizer so that it can be picked up by the [MosparoCaptchaValidator](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-mosparo-captcha-validator@1.1).

1.  You need to implement the interface [MosparoFormDefinitionInterface](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-mosparo-form-definition-interface@1.1) to provide a wrapper around your specific form structure.

    **EXT:my_extension/Classes/Domain/Model/Form/MyCustomFormDefinition.php**

    ```php
    namespace Vendor\MyExtension\Domain\Model\Form;

    use Denkwerk\MosparoForm\Domain\Model\Form\MosparoFormDefinitionInterface;
    use Vendor\MyExtension\Domain\Model\Form\YourFormDefinition;

    class MyCustomFormDefinition implements MosparoFormDefinitionInterface
    {
       public function __construct(protected YourFormDefinition $yourFormDefinition)
       {
       }

       public function getYourFormDefinition(): YourForm
       {
           return $this->yourFormDefinition;
       }

       public function setYourFormDefinition(YourForm $yourFormDefinition): void
       {
           $this->yourFormDefinition = $yourFormDefinition;
       }
    }
    ```
1.  Implement [FormNormalizerInterface](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-form-normalizer-interface@1.1) to create your form normalizer which handles your form structure accordingly.

    **EXT:my_extension/Classes/FormNormalizer/MyCustomFormNormalizer.php**

    ```php
    namespace Vendor\MyExtension\FormNormalizer;

    use Vendor\MyExtension\Domain\Model\Form\MyCustomFormDefinition;
    use Denkwerk\MosparoForm\Domain\Model\Form\MosparoFormDefinitionInterface;
    use Denkwerk\MosparoForm\Domain\Model\Dto\NormalizedData;
    use Denkwerk\MosparoForm\FormNormalizer\FormNormalizerInterface;

    final class MyCustomFormNormalizer implements FormNormalizerInterface
    {
       protected array $ignoredFieldTypes = [
           'password',
           'submit',
           'reset',
           // add your ignored field types here
       ];

       protected array $verifiableFieldTypes = [
           'input',
           'textarea',
           'select',
           // add your verifiable field types here
       ];

       public function supports(?MosparoFormDefinitionInterface $formDefinition): bool
       {
           // Return true if this normalizer should handle the given form definition
           return $formDefinition instanceof MyCustomFormDefinition;
       }

       public function normalize(array $postData, MosparoFormDefinitionInterface $formDefinition): NormalizedData
       {
           // Your normalization logic here...
           $normalized = new NormalizedData();
           // e.g. filter, transform $postData...
           $normalized->setFormData($filteredData);
           return $normalized;
       }
    }
    ```
1.  To register the normalizer service, add the following configuration to your custom provider extension [Services.yaml](https://docs.typo3.org/m/typo3/reference-coreapi/14.3/en-us/ExtensionArchitecture/FileStructure/Configuration/ServicesYaml.html#extension-configuration-services-yaml) file:

    **EXT:my_extension/Configuration/Services.yaml**

    ```yaml
    services:
       Vendor\MyExtension\FormNormalizer\MyCustomFormNormalizer:
         tags:
           - { name: 'mosparo.form_normalizer' }
    ```
1.  After implementing your own [MosparoFormDefinitionInterface](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-mosparo-form-definition-interface@1.1) and [FormNormalizerInterface](https://docs.typo3.org/permalink/denkwerk/mosparo-form:api-form-normalizer-interface@1.1), you typically want to integrate mosparo spam protection in your form validation logic.\
    For example, in a Powermail spam shield class, the mosparo captcha validator can be used like this:

    **EXT:mosparo-powermail/FormNormalizer/PowermailFormNormalizer.php**

    ```php
    use Denkwerk\MosparoForm\Validation\Validator\MosparoCaptchaValidator;
    use Vendor\Extension\Domain\Model\Form\PowermailMosparoFormDefinition;
    use TYPO3\CMS\Core\Utility\GeneralUtility;
    use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException;
    use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException;

    /**
     * mosparo spam protection
     *
     * @return bool
     * @throws ExtensionConfigurationExtensionNotConfiguredException
     * @throws ExtensionConfigurationPathDoesNotExistException
     */
    public function spamCheck(): bool
    {
        // Skip spam check if the mosparo field is not present or the check should be skipped
        if (!$this->hasMosparoField() || $this->shouldSkipMosaproValidatorCheck()) {
            return false;
        }

        /** @var MosparoCaptchaValidator $captchaValidator */
        $captchaValidator = GeneralUtility::makeInstance(MosparoCaptchaValidator::class);
        $captchaValidator->setOptions([
            'formDefinition' => new PowermailMosparoFormDefinition($this->mail->getForm()),
            'selectedProject' => $this->getSelectedProject(),
        ]);

        $captchaValidationResult = $captchaValidator->validate('');
        if (!$captchaValidationResult->hasErrors()) {
            // No spam detected
            return false;
        }

        // Spam detected
        return true;
    }
    ```
1.  That's it! Your custom form setup is now seamlessly protected by mosparo, ensuring all relevant fields are verified server-side.
