Finishers¶
The form framework ships a bunch of finishers, which will be briefly described here. For more details, please head to the API reference and check out the section regarding Finisher Options.
Closure finisher¶
The ‘Closure finisher’ can only be used within forms that are created programmatically. It allows you to execute your own finisher code without implementing/ declaring a finisher.
Confirmation finisher¶
The ‘Confirmation finisher’ is a simple finisher that outputs a given text after the form has been submitted.
DeleteUploads finisher¶
The ‘DeleteUploads finisher’ removes submitted files. Use this finisher, for example, after the email finisher if you do not want to keep the files within your TYPO3 installation.
Note
Finishers are only executed on successfully submitted forms. If a user uploads a file but does not finish the form successfully, the uploaded files will not be deleted.
Email finisher¶
The ‘Email finisher’ sends an email to one recipient. EXT:form uses two
EmailFinisher
declarations with the identifiers EmailToReceiver
and
EmailToSender
.
FlashMessage finisher¶
The ‘FlashMessage finisher’ is a simple finisher that adds a message to the FlashMessageContainer.
Redirect finisher¶
The ‘Redirect finisher’ is a simple finisher that redirects to another page. Additional link parameters can be added to the URL.
Note
This finisher stops the execution of all subsequent finishers in order to perform a redirect. Therefore, this finisher should always be the last finisher to be executed.
SaveToDatabase finisher¶
The ‘SaveToDatabase finisher’ saves the data of a submitted form into a database table.
Here is an example for adding uploads to ext:news (fal_related_files and fal_media).
-
identifier: SaveToDatabase
options:
-
table: tx_news_domain_model_news
mode: insert
elements:
my-field:
mapOnDatabaseColumn: bodytext
imageupload-1:
mapOnDatabaseColumn: fal_media
fileupload-1:
mapOnDatabaseColumn: fal_related_files
databaseColumnMappings:
pid:
value: 3
tstamp:
value: '{__currentTimestamp}'
datetime:
value: '{__currentTimestamp}'
crdate:
value: '{__currentTimestamp}'
hidden:
value: 1
-
table: sys_file_reference
mode: insert
elements:
imageupload-1:
mapOnDatabaseColumn: uid_local
skipIfValueIsEmpty: true
databaseColumnMappings:
table_local:
value: sys_file
tablenames:
value: tx_news_domain_model_news
fieldname:
value: fal_media
tstamp:
value: '{__currentTimestamp}'
crdate:
value: '{__currentTimestamp}'
showinpreview:
value: 1
uid_foreign:
value: '{SaveToDatabase.insertedUids.0}'
-
table: sys_file_reference
mode: insert
elements:
fileupload-1:
mapOnDatabaseColumn: uid_local
skipIfValueIsEmpty: true
databaseColumnMappings:
table_local:
value: sys_file
tablenames:
value: tx_news_domain_model_news
fieldname:
value: fal_related_files
tstamp:
value: '{__currentTimestamp}'
crdate:
value: '{__currentTimestamp}'
uid_foreign:
value: '{SaveToDatabase.insertedUids.0}'
-
table: sys_file_reference
mode: update
whereClause:
uid_foreign: '{SaveToDatabase.insertedUids.0}'
uid_local: 0
databaseColumnMappings:
pid:
value: 0
uid_foreign:
value: 0
Write a custom finisher¶
If you want to make the finisher configurable in the backend UI read here.
Finishers are defined as part of a prototype
within a
finishersDefinition
. The property implementationClassName
is to be
utilized to load the finisher implementation.
TYPO3:
CMS:
Form:
prototypes:
standard:
finishersDefinition:
CustomFinisher:
implementationClassName: 'VENDOR\MySitePackage\Domain\Finishers\CustomFinisher'
If the finisher requires options, you can define those within the
options
property. The options will be used as default values and can
be overridden using the form definition
.
Define the default value:
TYPO3:
CMS:
Form:
prototypes:
standard:
finishersDefinition:
CustomFinisher:
implementationClassName: 'VENDOR\MySitePackage\Domain\Finishers\CustomFinisher'
options:
yourCustomOption: 'Ralf'
Override the option using the form definition
:
identifier: sample-form
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: CustomFinisher
options:
yourCustomOption: 'Björn'
renderables:
...
Each finisher has to be programmed to the interface TYPO3\CMS\Form\Domain\Finishers\FinisherInterface
and should extend the class TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher
.
In doing so, the logic of the finisher should start with the method
executeInternal()
.
Accessing finisher options¶
If your finisher extends TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher
,
you can access your finisher options with the help of the parseOption()
method:
$yourCustomOption = $this->parseOption('yourCustomOption');
parseOption()
is looking for ‘yourCustomOption’ in your
form definition
. If it cannot be found, the method checks
the
prototype
configuration for a default value,the finisher class itself by searching for a default value within the
$defaultOptions
property:declare(strict_types = 1); namespace VENDOR\MySitePackage\Domain\Finishers; class CustomFinisher extends \TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher { protected $defaultOptions = [ 'yourCustomOption' => 'Olli', ]; // ... }
If the option cannot be found by processing this fallback chain, null
is
returned.
If the option is found, the process checks whether the option value will
access FormRuntime values.
If the FormRuntime
returns a positive result, it is checked whether the
option value can access values of preceding finishers.
At the very end, it tries to translate the finisher options.
Accessing form runtime values¶
By utilizing a specific notation, finisher options can be populated with
submitted form values (assuming you are using the parseOption()
method).
You can access values of the FormRuntime
and thus values of each single
form element by encapsulating the option values with {}
. If there is a
form element with the identifier
‘subject’, you can access its value
within the finisher configuration. Check out the following example to
get the whole idea.
identifier: simple-contact-form
label: 'Simple Contact Form'
prototype: standard
type: Form
finishers:
-
identifier: Custom
options:
yourCustomOption: '{subject}'
renderables:
-
identifier: subject
label: 'Subject'
type: Text
// $yourCustomOption contains the value of the form element with the
// identifier 'subject'
$yourCustomOption = $this->parseOption('yourCustomOption');
In addition, you can use {__currentTimestamp}
as a special option value.
It will return the current UNIX timestamp.
Finisher Context¶
The class TYPO3\CMS\Form\Domain\Finishers\FinisherContext
takes care of
transferring a finisher context to each finisher. Given the finisher is
derived from TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher
the
finisher context will be available via:
$this->finisherContext
The method cancel
prevents the execution of successive finishers:
$this->finisherContext->cancel();
The method getFormValues
returns all of the submitted form values.
getFormValues
:
$this->finisherContext->getFormValues();
The method getFormRuntime
returns the FormRuntime
:
$this->finisherContext->getFormRuntime();
Add finisher to backend UI¶
After adding a custom finisher you can also add the finisher to the form editor GUI to let your backend users configure it visually. Add the following to the backend yaml setup:
TYPO3:
CMS:
Form:
prototypes:
standard:
formElementsDefinition:
Form:
formEditor:
editors:
900:
# Extend finisher drop down
selectOptions:
35:
value: 'CustomFinisher'
label: 'Custom Finisher'
propertyCollections:
finishers:
# add finisher fields
25:
identifier: 'CustomFinisher'
editors:
__inheritances:
10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
100:
label: "Custom Finisher"
# custom field (input, required)
110:
identifier: 'customField'
templateName: 'Inspector-TextEditor'
label: 'Custom Field'
propertyPath: 'options.customField'
propertyValidators:
10: 'NotEmpty'
# email field
120:
identifier: 'email'
templateName: 'Inspector-TextEditor'
label: 'Subscribers email'
propertyPath: 'options.email'
enableFormelementSelectionButton: true
propertyValidators:
10: 'NotEmpty'
20: 'FormElementIdentifierWithinCurlyBracesInclusive'
finishersDefinition:
CustomFinisher:
formEditor:
iconIdentifier: 'form-finisher'
label: 'Custom Finisher'
predefinedDefaults:
options:
customField: ''
email: ''
# displayed when overriding finisher settings
FormEngine:
label: 'Custom Finisher'
elements:
customField:
label: 'Custom Field'
config:
type: 'text'
email:
label: 'Subscribers email'
config:
type: 'text'
Make sure the setup file is registered in the backend:
module.tx_form.settings.yamlConfigurations {
123456789 = EXT:yourExtension/Configuration/Form/Backend.yml
}