Skip to content

Commit

Permalink
Merge pull request #1738 from thomasnares/customizeCrud
Browse files Browse the repository at this point in the history
Document forms with Symfony
  • Loading branch information
kpodemski authored Nov 8, 2023
2 parents 8b1fa2c + c1596d5 commit b322d4a
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 24 deletions.
274 changes: 271 additions & 3 deletions development/components/form/_index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,276 @@
---
title: The Form component
title: Forms with Symfony
menuTitle: Form
useMermaid: true
---

# The Form component
# Forms with Symfony

{{% children /%}}
## 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`](#controllers) - Handles all logic for the `Form`
- [`FormBuilder`](#form-builder) - Builds the `Form`, and provide data to the `Form`
- [`FormDataProvider`](#form-data-provider) - Provides data to the `Form`
- [`Form`](#form-types) - Defines the `Form` and its content
- [`Form Theme`](#form-theme) - Allows to customise the style of the `Form`
- `Form Templates` - Allows to render the `Form` with `Twig`
- [`FormHandler`](#form-handler) - Handles the submited `Form`, provides data to the `FormDataHandler`, and handles `FormHandlerResult`
- [`FormDataHandler`](#form-handler) - Persists the data to `Database`

<div class='mermaid'>
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
</div>

### 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.

### Form Types

Developers have access to a wide range of field types ([see Symfony types](https://symfony.com/doc/4.4/reference/forms/types.html)) 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]({{< relref "/8/development/components/form/types-reference">}}).

### 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.

{{% notice note %}}
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]({{<relref "/8/modules/concepts/doctrine/#using-doctrine">}}), using [ObjectModel entities]({{<relref "/8/development/components/database/objectmodel">}}), or using [CQRS concepts]({{<relref "/8/development/architecture/domain/cqrs">}}).
{{% /notice %}}

### Form Theme

To render forms in a clean and user-friendly way, PrestaShop extended Symfony's [Bootstrap 4 Form Theme][sf-bootstrap4-form-theme] to create **PrestaShop UI Kit Form theme**. Learn more on [Symfony Form theme for PrestaShop]({{< relref "/8/development/components/form/form-theme/form-theme">}})

## 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.

<div class='mermaid'>
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
</div>

### 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](https://github.com/PrestaShop/example-modules/blob/master/demoproductform2/src/Form/Modifier/ProductFormModifier.php).

```php
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 {
[...]
}
```

```yml
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 [action<Object>FormBuilderModifier]({{<relref "/8/modules/concepts/hooks/list-of-hooks/action<FormName>FormBuilderModifier">}}).

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

```php
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]({{<relref "/8/modules/concepts/hooks/list-of-hooks/action<FormName>FormDataProviderDefaultData">}}) allows your module to provide or modify the default values sent to forms.

```php
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](https://github.com/PrestaShop/example-modules/tree/master/demoformdataproviders).

### FormDataProviderData Hook

The [`Action<FormName>FormDataProviderData` hook]({{<relref "/8/modules/concepts/hooks/list-of-hooks/action<FormName>FormDataProviderData">}}) allows your module to provide or modify the values sent to forms.

```php
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](https://github.com/PrestaShop/example-modules/tree/master/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 |

<div class="mermaid">
graph TD
subgraph "Update"
BA(actionBeforeUpdate<strong>FormName</strong>FormHandler) --> BB(IdentifiableObject updated)
BB --> BC(actionAfterUpdate<strong>FormName</strong>FormHandler)
end
subgraph "Create"
A(actionBeforeCreate<strong>FormName</strong>FormHandler) --> B(IdentifiableObject created)
B --> C(actionAfterCreate<strong>FormName</strong>FormHandler)
end
</div>

It as been implemented in an example module: [DemoProductForm](https://github.com/PrestaShop/example-modules/blob/master/demoproductform/demoproductform.php).

```php
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]({{<relref "/8/modules/sample-modules/extending-sf-form-with-upload-image-field" >}}) |
| How to extend the new product page form | [Tutorial]({{<relref "/8/modules/sample-modules/extend-product-page" >}}) |
| How to modify Customers grid | [Tutorial]({{<relref "/8/modules/sample-modules/grid-and-identifiable-object-form-hooks-usage" >}}) |

[sf-bootstrap4-form-theme]: https://symfony.com/doc/4.4/form/bootstrap4.html
18 changes: 8 additions & 10 deletions development/components/form/form-theme/form-pages.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
---
title: List of forms
title: List of migrated forms
weight: 20
---

# List of forms
# List of migrated forms

This section states when a page that contains a Symfony form has been migrated to Symfony, and when the form has been simplified to rely on the PrestaShop UI Kit Form theme.

You can find the detailed status of the project on the [GitHub tracking issue][form-theme-simplification-project].

[form-theme-simplification-project]: https://github.com/PrestaShop/PrestaShop/issues/16482


Page | Migrated in version | Form simplified in version
--------------------------|:-------------------------:|:-----------------------------:
**Advanced Parameters** | |
Expand All @@ -25,9 +24,9 @@ Advanced Parameters > Webservice | 1.7.5 |
Advanced Parameters > Webservice > Add new webservice key | 1.7.6 | 1.7.8
Advanced Parameters > Email | 1.7.5 | 1.7.8
Advanced Parameters > Database > SQL Manager | 1.7.5 | 1.7.8
Advanced Parameters > Team > Employees | 1.7.6 | 8.0
Advanced Parameters > Team > Employees > Add new employee | 1.7.6 | 8.0
Advanced Parameters > Team > Employees > Profiles > Add new profile | 1.7.6 | 8.0
Advanced Parameters > Team > Employees | 1.7.6 | 8.1
Advanced Parameters > Team > Employees > Add new employee | 1.7.6 | 8.1
Advanced Parameters > Team > Employees > Profiles > Add new profile | 1.7.6 | 8.1
Advanced Parameters > Team > Employees > Permissions | |
Advanced Parameters > Multistore > Add new / edit shop group | |
Advanced Parameters > Multistore > Add a new shop | |
Expand Down Expand Up @@ -59,7 +58,7 @@ Shop parameters > Search > Search > Add new / edit alias | |
Shop parameters > Search > Tags > Add new / edit tag | |
| |
**Catalog** | |
Orders > Invoices | 1.7.5 | 8.0
Orders > Invoices | 1.7.5 |
Orders > Delivery Slips | 1.7.5 | 8.0
Orders > Credit Slips | 1.7.7 | 1.7.8
Shipping > Preferences | 1.7.5 | 1.7.8
Expand All @@ -74,15 +73,15 @@ Catalog > Attributes & Features > Add new / edit attribute | |
Catalog > Attributes & Features > Add new / edit attribute value | |
Catalog > Discounts > Cart rules > Add new / edit catalog price rule | |
Catalog > Discounts > Cart rules > Add new / edit cart rule | |
Payment > Preferences | 1.7.5 | 8.0
Payment > Preferences | 1.7.5 |
Customers > Addresses | 1.7.7 | 1.7.8
Customers > Addresses > Add new / edit address | 1.7.7 | 1.7.8
Customers > Customers | 1.7.6 | 1.7.8
Customers > Customers > View customer | 1.7.6 | 1.7.8
Customers > Customers > Add new / edit customer | 1.7.6 | 1.7.8
Customer Service > Order Messages > Add new / edit order message | 1.7.7 | 1.7.8
Customer Service > Customer Service | |
Customer Service > Merchandise Returns > Edit | 8.0 | 8.0
Customer Service > Merchandise Returns > Edit | 8.0 |
| |
**Improve** | |
Design > Theme & Logo > Add new theme | 1.7.6 | 1.7.8
Expand All @@ -106,4 +105,3 @@ International > Taxes > Taxes | 1.7.6 |
International > Taxes > Taxes > Add new / edit tax | 1.7.6 | 1.7.8
International > Taxes > Tax Rules | |
International > Taxes > Tax Rules > Add new / edit tax rule group | |

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ check_exceptions: false
chain: false
origin: core
description: ''

---

{{% hookDescriptor %}}
Expand All @@ -33,3 +32,7 @@ $this->hookDispatcher->dispatchWithParameters(
]
);
```

## Example implementation

This hook has been implemented as an example in our [example-modules repository](https://github.com/PrestaShop/example-modules/tree/master/demoformdataproviders).
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ check_exceptions: false
chain: false
origin: core
description: ''

---

{{% hookDescriptor %}}
Expand All @@ -31,3 +30,7 @@ dispatchWithParameters(
]
)
```

## Example implementation

This hook has been implemented as an example in our [example-modules repository](https://github.com/PrestaShop/example-modules/tree/master/demoformdataproviders).
Loading

0 comments on commit b322d4a

Please sign in to comment.