Configuration

A lot of configuration. Why?

The requirements for building forms in a declarative and programmatic way are complex. What we have learned so far is that the program code must be kept as generic as possible to handle the dynamics of forms, but a generic program code means a lot of configurative overhead.

Initially, the configuration may overwhelm you, but it also has some great advantages. Many aspects of EXT:form can be manipulated in a purely configurative manner without involving a developer.

Furthermore, we wanted to avoid the configuration being done at places whose context actually suggests something different. This pedantry, however, leads to the situation in which certain settings have to be defined multiple times at multiple places. This may seem nonsensical, but it avoids unpredictable behaviour. Within the form framework, nothing happens magically. It is all about configuration.

Why YAML?

Former versions of EXT:form used a subset of TypoScript to describe the definition of a specific form and the behaviour of the included form elements. This led to a lot of confusion from integrators because the implemented definition language looked like TypoScript but did not behave like TypoScript.

Since the definition of forms and form elements must be declarative, the EXT:form team decided to use YAML. Just through the visual appearance of YAML, it should be clear to everyone that neither magic nor TypoScript stdWrap functionality are possible.

YAML registration

At the moment, configuration via YAML is not natively integrated into the core of TYPO3. You have to make a short detour by using TypoScript in order to register your YAML configuration. Furthermore, there is a "speciality" regarding the integration of your YAML configuration for the backend module.

YAML registration for the frontend

For the frontend the whole YAML configuration is loaded via the following TypoScript (see EXT:form/Configuration/TypoScript/setup.typoscript):

plugin.tx_form {
    settings {
        yamlConfigurations {
            10 = EXT:form/Configuration/Yaml/FormSetup.yaml
        }
    }
}
Copied!

Since the key 10 is already taken, we recommend registering your own configuration beginning with the key 100.

plugin.tx_form {
    settings {
        yamlConfigurations {
            100 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
        }
    }
}
Copied!

YAML registration for the backend

For the backend the whole YAML configuration is loaded via the following TypoScript in EXT:form/ext_localconf.php.

EXT:form/ext_localconf.php
ExtensionManagementUtility::addTypoScriptSetup('
    module.tx_form {
       settings {
           yamlConfigurations {
               10 = EXT:form/Configuration/Yaml/FormSetup.yaml
           }
       }
    }
');
Copied!

Since the key 10 is already taken, we recommend registering your own configuration beginning with a unique number (for instance current timestamp) in a EXT:my_extension/ext_localconf.php file:

EXT:my_extension/ext_localconf.php
<?php

declare(strict_types=1);

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTypoScriptSetup('
    module.tx_form {
       settings {
           yamlConfigurations {
               1732785702 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
           }
       }
    }
');
Copied!

The backend module of EXT:form is based on Extbase. Such backend modules can, like frontend plugins, be configured via TypoScript. The frontend plugins are configured below plugin.tx_form. For the configuration of the backend module.tx_form is used.

The recommended way to include "global" backend TypoScript is by using the API function \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup() in a EXT:my_extension/ext_localconf.php file. This registers TypoScript as "global" snippet: TypoScript is usually bound to pages and page uids in some way - via sys_template records, or via site sets. The form backend module however is not bound to a page, and the extbase backend bootstrap did a lot of magic in the past to find an appropriate page if none is given.

YAML loading

TYPO3 is using a custom 'YAML loader' for handling YAML in TYPO3 based on the Symfony YAML package. This YAML loader is able to resolve environment variables. In addition, EXT:form comes with an own YAML loader which has some limitations/ restrictions, especially when it comes to resolve environment variables. Those limitations are necessary security-wise.

EXT:form differentiates between form configuration and form definition. In addition, a form definition can be stored in the file system (FAL) or can be shipped with a custom extension. Depending on those parameters different YAML loaders are used and thus, different features are available.

YAML file

YAML loader

YAML configuration

TYPO3 core

YAML definition stored in file system (default when using the form editor)

TYPO3 Form Framework

YAML definition stored in custom extension

TYPO3 core

Configuration aspects

In EXT:form, four aspects can be configured:

  • the behaviour of the frontend rendering,
  • the behaviour of the form editor,
  • the behaviour of the form manager, and
  • the behaviour of the form plugin.

Those aspects are defined in separate files which are only loaded in the frontend/ backend when needed. This approach has two advantages:

  • increased clarity,
  • increased performance, e.g. the form editor configuration is not needed in the frontend and therefore not loaded.

It is up to you if you want to follow this guideline or if you want to put the whole configuration into one large file.

There are some configurational aspects which cannot explicitly be assigned to either the frontend or the backend. Instead, the configuration is valid for both areas. For example, within the backend, the whole frontend configuration is required in order to allow the form preview to work properly. In addition, as soon as the form is rendered via the `form plugin``, the ``FormEngine` configuration is needed to interpret the overridden finisher configuration correctly.

Inheritances

The final YAML configuration is not based on one huge file. Instead, it is a compilation of a sequential process:

  • First of all, all registered configuration files are parsed as YAML and are overlaid according to their order.
  • After that, the __inheritances operator is applied. It is a unique operator introduced by the form framework.
  • Finally, all configuration entries with a value of null are deleted.

Additionally, the frontend configuration can be extended/ overridden by TypoScript:

plugin.tx_form {
    settings {
        yamlSettingsOverrides {
            ...
        }
    }
}
Copied!

For example, if you want to override the fluid templates and you therefore register an additional configuration file via

plugin.tx_form {
    settings {
        yamlConfigurations {
            # register your own additional configuration
            # choose a number higher than 30 (below is reserved)
            100 = EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml
        }
    }
}
Copied!

... you only have to define the following YAML setup in EXT:my_site_package/Configuration/Form/CustomFormSetup.yaml:

prototypes:
  standard:
    formElementsDefinition:
      Form:
        renderingOptions:
          templateRootPaths:
            20: 'EXT:my_site_package/Resources/Private/Templates/Form/Frontend/'
          partialRootPaths:
            20: 'EXT:my_site_package/Resources/Private/Partials/Form/Frontend/'
          layoutRootPaths:
            20: 'EXT:my_site_package/Resources/Private/Layouts/Form/Frontend/'
Copied!

The values of your own configuration file will overrule the corresponding values of the basic configuration file (EXT:form/Configuration/Yaml/FormSetup.yaml).

__inheritances operator

The __inheritances operator is an extremely useful instrument. Using it helps to significantly reduce the configuration effort. It behaves similar to the < operator in TypoScript. That is, the definition of the source object is copied to the target object. The configuration can be inherited from several parent objects and can be overridden afterwards. Two simple examples will show you the usage and behaviour of the __inheritances operator.

Form:
  part01:
    key01: value
    key02:
      key03: value
  part02:
    __inheritances:
      10: Form.part01
Copied!

The configuration above results in:

Form:
  part01:
    key01: value
    key02:
      key03: value
  part02:
    key01: value
    key02:
      key03: value
Copied!

As you can see, part02 inherited all of part01's properties.

Form:
  part01:
    key: value
  part02:
    __inheritances:
      10: Form.part01
    key: 'value override'
Copied!

The configuration above results in:

Form:
  part01:
    key: value
  part02:
    key: 'value override'
Copied!

EXT:form heavily uses the __inheritances operator, in particular, for the definition of form elements. The following example shows you how to use the operator to define a new form element which behaves like the parent element but also has its own properties.

prototypes:
  standard:
    formElementsDefinition:
      GenderSelect:
        __inheritances:
          10: 'prototypes.standard.formElementsDefinition.RadioButton'
        renderingOptions:
          templateName: 'RadioButton'
        properties:
          options:
            f: 'Female'
            m: 'Male'
            u: 'Unicorn'
            a: 'Alien'
Copied!

The YAML configuration defines a new form element called GenderSelect. This element inherits its definition from the RadioButton element but additionally ships four predefined options. Without any problems, the new element can be used and overridden within the form definition.

It will probably take some time to fully understand the awesomeness of this operator. If you are eager to learn more about this great instrument, check out the unit tests defined in EXT:form/Tests/Unit/Mvc/Configuration/InheritancesResolverServiceTest.php.

Prototypes

Most of the configurational aspects of the form framework are defined in so-called prototypes. By default, EXT:form defines a prototype named standard. The definition of form elements - including their rendering in the frontend, form editor and form plugin - reside within those prototypes. As soon as you create a new form, the specific form definition references such a prototype.

This allows you to do a lot of nifty stuff. Let your imagination run free. For example:

  • based on the referenced prototype, the same form can load

    • ...varying templates
    • ...varying form editor configurations
    • ...varying form plugin finisher overrides
  • within the form manager, depending on the selected prototype

    • ...varying form editor configurations can be loaded
    • ...varying pre-configured form templates (boilerplates) can be chosen
  • different prototypes can define different/ extended form elements and display them in the frontend/ form editor accordingly

Check out the following use case to fully understand the concept behind prototypes. Imagine that there are two defined prototypes: "noob" and "poweruser".

Prototype "noob"

Prototype "poweruser"

Available form elements within the ``form editor``

Text, Textarea

No changes. Default behaviour.

Available finisher within the ``form editor``

Only the email finisher is available. It offers a field for setting the subject of the mail. All remaining fields are hidden and filled with default values.

No changes. Default behaviour.

Finisher overrides within the ``form plugin``

It is not possible to override the finisher configuration.

No changes. Default behaviour.