Forms with Symfony

Introduction

Symfony’s Forms system separates the construction, rendering, and processing of forms. This involves multiple key concepts, including form creation, data provision, validation, data persistence from submitted forms, and form rendering.

  • Controllers - Handles all logic for the Form
  • FormBuilder - Builds the Form, and provide data to the Form
  • Form - Defines the Form and its content
  • Form Theme - Allows to customise the style of the Form
  • Form Templates - Allows to render the Form with Twig
  • FormHandler - Handles the submited Form, provides data to the FormDataHandler, and handles FormHandlerResult
graph AA(Http Request)-.->A subgraph Core subgraph Admin Theme K(Form Theme) end subgraph Render form A(Controller)-->O(FormBuilder) O-->|1|M(FormDataProvider) M-->|2|O O-->|3|P(Form) P-->F(Rendered Form) K-->F A-->Q(Form Templates) Q-->F end subgraph Handle form F -- Submit -->G(Controller) G-->H(FormHandler) H-->|Persist|Z(FormDataHandler) end end G-.->ZZ(Redirect) style F fill:#f9f,stroke:#333,stroke-width:2px style Core fill:#fbfbea

Controllers

Controllers in the back office receive HTTP requests. When handling requests for creating or editing an entity, they instantiate and utilize several concepts.

FormBuilder

In most scenarios, the Controller initially instantiates the FormBuilder. This component constructs the necessary Form, incorporating all Form Types. It also retrieves data from the FormDataProvider, ensuring that the form is populated with the appropriate data. The FormBuilder will build the required Form with all Form Types, and retrieve data from the FormDataProvider.

FormDataProvider

The FormDataProvider is responsible for retrieving data from the Database. This data is used to populate the form when editing an existing entity. Additionally, it provides default values for the form fields, both when creating a new entity and when editing an existing one.

Learn more about CRUD forms and FormDataProviders.

Form Types

Developers have access to a wide range of field types (see Symfony types) that come from the Symfony framework. Additionally, PrestaShop enhances this selection with its own reusable field types.

A complete reference of PrestaShop form types can be found here.

FormHandler

The FormHandler is responsible of:

  • extracting the form data,
  • triggering the actionBeforeCreate<FormName>FormHandler, actionBeforeUpdate<FormName>FormHandler, actionAfterCreate<FormName>FormHandler and actionAfterUpdate<FormName>FormHandler hooks
  • calling the FormDataHandler
  • returning a FormHandlerResult

FormDataHandler

The FormDataHandler is tasked with persisting data to the Database. It accomplishes this by dispatching a CQRS command to the command bus, ensuring efficient and effective data handling.

This page focuses on migrated Forms utilizing Symfony and the CQRS pattern.

It’s important to note that data can be persisted in several ways, including: Doctrine entities, using ObjectModel entities, or using CQRS concepts.

Form Theme

To render forms in a clean and user-friendly way, PrestaShop extended Symfony’s Bootstrap 4 Form Theme to create PrestaShop UI Kit Form theme. Learn more on Symfony Form theme for PrestaShop

Customize forms by modules

Concepts

  • FormModifier: This component is used to modify the Form, primarily for module integration.
  • FormModifier hook_: Connects the FormModifier to the form, enabling modifications.
  • FormDataProviderData hook: Facilitates the provision of specific data to the form.
  • FormDataProviderDefaultData hook: Used for supplying default data to the form.
  • FormHandler hook: Allows the module to handle the form and its data effectively.
graph AA(Http Request)-.->A subgraph Core subgraph Admin Theme K(Form Theme) end subgraph Render Form A(Controller)-->O(FormBuilder) O-->|1|M(FormDataProvider) M-->|2|O O-->|3|P(Form) P-->F(Rendered Form) K-->F A-->Q(Form Templates) Q-->F end subgraph Handle Form F -- Submit -->G(Controller) G-->H(FormHandler) H-->|"Persist"|Z(FormDataHandler) end end subgraph Module I(FormModifier) -->S(FormModifier hook) S -.-> O H -.-> J(FormHandler hooks) T(FormDataProviderDefaultData hook) -.-> M U(FormDataProviderData hook) -.-> M end G-.->ZZ(Redirect) style F fill:#f9f,stroke:#333,stroke-width:2px style Module fill:#dcedfc style Core fill:#fbfbea

FormModifier

A FormModifier (also known as FormBuilderModifier) allows you to alter the contents of a Form. It is particularly useful within modules, allowing developers to add, modify, or remove elements from the form as required.

It’s been implemented in an example module: DemoProductForm2.

namespace PrestaShop\Module\DemoProductForm\Form\Modifier;
[...]

final class ProductFormModifier
{
    public function __construct(
        private readonly TranslatorInterface $translator,
        private readonly FormBuilderModifier $formBuilderModifier
    ) {
    }

    public function modify(
        ?ProductId $productId,
        FormBuilderInterface $productFormBuilder
    ): void {
        [...]        
    }
services:
    PrestaShop\Module\DemoProductForm\Form\Modifier\ProductFormModifier:
        autowire: true
        public: true
        arguments:
            $formBuilderModifier: '@form.form_builder_modifier'

FormModifier hook

A FormModifier by itself will not affect a Form unless it is properly hooked. To ensure functionality, the FormModifier must be linked by implementing the actionFormBuilderModifier.

In the module, register the hook and implement the method, for example, for the Product entity:

public function install()
{
    $this->registerHook('actionProductFormBuilderModifier');
}

public function hookActionProductFormBuilderModifier(array $params): void
{
    /** @var ProductFormModifier $productFormModifier */
    $productFormModifier = $this->get(ProductFormModifier::class);
    $productId = isset($params['id']) ? new ProductId((int) $params['id']) : null;

    $productFormModifier->modify($productId, $params['form_builder']);
}

FormDataProviderDefaultData Hook

The Action<FormName>FormDataProviderDefaultData hook allows your module to provide or modify the default values sent to forms.

public function install()
{
    $this->registerHook('actionProductFormDataProviderDefaultData');
}

public function hookActionProductFormDataProviderDefaultData(array $params): void
{
    // define default values for fields in $params["data"]
}

This hook has been implemented as an example in our example-modules repository.

FormDataProviderData Hook

The Action<FormName>FormDataProviderData hook allows your module to provide or modify the values sent to forms.

public function install()
{
    $this->registerHook('actionProductFormDataProviderData');
}

public function hookActionProductFormDataProviderData(array $params): void
{
    // change value of fields in $params["data"]
}

This hook has been implemented as an example in our example-modules repository - demoformdataproviders.

FormHandler hook

The FormHandler hook is designed to intercept Form data, enabling the execution of various operations with this data. Its primary function is to facilitate the persistence of form data, ensuring that the data captured by the form can be effectively processed and stored later.

There are 4 hooks available to work with the Form data:

Hook Description
actionBeforeCreate<FormName>FormHandler Triggered on creation of an IdentifiableObject, before persisting it to Database
actionBeforeUpdate<FormName>FormHandler Triggered on update of an IdentifiableObject, before persisting it to Database
actionAfterCreate<FormName>FormHandler Triggered on creation of an IdentifiableObject, after persisting it to Database
actionAfterUpdate<FormName>FormHandler Triggered on update of an IdentifiableObject, after persisting it to Database
graph TD subgraph "Update" BA(actionBeforeUpdateFormNameFormHandler) --> BB(IdentifiableObject updated) BB --> BC(actionAfterUpdateFormNameFormHandler) end subgraph "Create" A(actionBeforeCreateFormNameFormHandler) --> B(IdentifiableObject created) B --> C(actionAfterCreateFormNameFormHandler) end

It as been implemented in an example module: DemoProductForm.

public function hookActionAfterUpdateCombinationFormFormHandler(array $params): void
{
    $combinationId = $params['form_data']['id'];
    [...]
}

Examples

You can find some examples of how to use forms in your modules to extend PrestaShop capabilities:

Subject Link
Extend a Symfony form in a module with an upload field for image Tutorial
How to extend the new product page form Tutorial
How to modify Customers grid Tutorial