Creating Form Components
Most Flux Forms are declared directly in Fluid using the Form ViewHelpers. This has the advantage that the form definition and the template which uses its values are stored together. There are, however, cases where creating the Form in PHP is more practical. A shared collection of fields, a form assembled from configuration or a custom Provider are typical examples.
The PHP objects use the same hierarchy as the ViewHelpers. A Form contains
fields and containers, a Sheet groups components on a tab and a Section
contains repeatable objects. This means that a form can be moved from Fluid to
PHP without changing the basic way its values are stored.
The following small factory creates a form with a single sheet and two fields:
<?php
declare(strict_types=1);
namespace Vendor\Site\Form;
use FluidTYPO3\Flux\Form;
use FluidTYPO3\Flux\Form\Container\Sheet;
use FluidTYPO3\Flux\Form\Field\Input;
use FluidTYPO3\Flux\Form\Field\Text;
final class TeaserFormFactory
{
public function create(): Form
{
$form = Form::create([
'name' => 'teaser',
'label' => 'Teaser',
]);
$contentSheet = $form->createContainer(
Sheet::class,
'content',
'Content',
);
$contentSheet->createField(
Input::class,
'headline',
'Headline',
);
$contentSheet->createField(
Text::class,
'text',
'Text',
);
return $form;
}
}
Form::create() is the root of the definition. createContainer() and
createField() receive the component class, the component name and an optional
label. The name is the technical identifier used when values are saved and
later made available to the template. It should therefore be unique among
sibling components and should not be changed after the form has been used for
real content. The label is intended for editors and can be translated or
changed without affecting stored values.
Containers and fields can be nested in the same way as their Fluid
counterparts. For example, a Section can create one or more SectionObject
containers, and each object can create its own fields. Keeping the hierarchy
shallow where possible makes both the backend form and the resulting variable
structure easier to understand.
A Provider can return the Form built by such a factory from its form-building method. Keeping the construction in a separate class is useful when several Providers share components, and also makes the structure easier to test without rendering a complete backend form.
Creating a new component class is only necessary when the existing fields and containers cannot describe the required behaviour. In most cases composing the built-in components is simpler. If a custom class is required, derive it from the closest matching Flux field or container, keep its public configuration small and make sure the generated TYPO3 field configuration is valid for every supported value.
Note: Form objects describe the editing interface and the structure of stored values. Validation and transformation which belong to the application's business rules should remain in dedicated services instead of being hidden in the form factory.