API Forms
Introduction
API Forms are a powerful feature of the OpenDXP Headless Bundle, enabling the definition, validation, and processing of forms via API endpoints. They offer a flexible way to use and manage forms in OpenDXP's headless mode.
Core Concepts
API Forms consist of several core components:
- Form Type: The Symfony Form Type class that defines the form
- Form Data Resolver: A service that provides the data for the form
- Form Options Resolver: Optional – A service that provides additional options for the form
- Validation Groups: Optional – Defines validation groups for the form
- Form Field Priority: Optional – Allows sorting of form fields
Configuration
In YAML Configuration
The basic configuration of an API Form is done in the file: headless.yaml
opendxp_headless:
api_forms:
demo_form_schema: # Unique name of the form
form_type: App\Form\Type\DemoApiForm # The Symfony Form Type class
form_data_resolver: OpenDxp\Bundle\HeadlessBundle\Form\FormDataResolver\NullDataResolver # Data resolver
form_data_resolver_config: { className: 'User' } # Optional configuration for the resolver
form_data_voter: 'APP_DEMO_FORM' # Optional voter for access control
attributes: # Field priorities (optional)
field1:
priority: 10
field2.subfield:
priority: 5
Form Data Resolver
Form Data Resolvers are services that provide the initial data for the form. The bundle includes several predefined resolvers:
- NullDataResolver: Returns
null(for new entities) - DataObjectDataResolver: Creates or loads a OpenDXP data object
- ArrayDataResolver: Returns an empty array
Example configuration for a DataObjectDataResolver:
form_data_resolver: OpenDxp\Bundle\HeadlessBundle\Form\FormDataResolver\DataObjectDataResolver
form_data_resolver_config:
className: 'CoreShopCustomer' # OpenDXP class name
identifier: 'id' # Optional: Parameter for identification (default: 'id')
checkRouteParams: true # Optional: Checks route parameters (default: true)
Implementing Custom Form Data Resolvers
To create a custom form data resolver, implement the FormDataResolverInterface:
namespace App\Form\FormDataResolver;
use OpenDxp\Bundle\HeadlessBundle\Context\HeadlessContextStack;
use OpenDxp\Bundle\HeadlessBundle\Form\FormDataResolver\FormDataResolverInterface;
use Symfony\Component\HttpFoundation\Request;
final class CustomDataResolver implements FormDataResolverInterface
{
public function resolve(HeadlessContextStack $contextStack, Request $request, array $config): mixed
{
// Implement your logic
return $yourData;
}
}
You must then register your resolver as a service and tag it with headless.resolver.form_data.
Usage in Controllers
Using the MapApiForm Attribute
The MapApiForm attribute allows direct integration of API Forms into controller methods:
#[Route(path: '/demo-form-action', name: 'demo_form_action', methods: ['POST'])]
final class DemoFormCommandAction extends ApiCommandAction
{
public function __invoke(
#[MapApiForm(
formType: DemoApiForm::class,
formDataResolver: NullDataResolver::class,
validationGroups: ['app:api:form']
)]
DemoFormCommand $demoCommand,
): JsonResponse {
return $this->handleWithResponse($demoCommand);
}
}
Using the SchemaRoute Attribute
The bundle also allows forms to be integrated via a reference:
#[AsController]
#[SchemaRoute(reference: '#/api_forms/demo_form_schema')]
#[Route(path: '/demo-form-query/{id}', name: 'demo_form_query', methods: ['GET'])]
final class DemoFormQueryAction extends ApiQueryAction
{
// Controller method
}
Dynamic Form Schema
The bundle automatically generates schema endpoints for configured forms. For a form named demo_form_schema, an endpoint is created at /demo-form-schema/schema.
This schema includes all information about the form, including:
- Field types
- Validation rules
- Select options
- Field priorities
FormBuilder Integration
The bundle also integrates with the OpenDXP FormBuilder:
parameters:
headless_enable_form_builder_area_brick: false # Default
FormBuilder forms are automatically detected and made available at the endpoint /form/{alias}.
Field Priorities
Form fields can be assigned priorities to control their order:
attributes:
address:
priority: 198
address.street:
priority: 4
firstname:
priority: 193
lastname:
priority: 192
Fields are sorted from highest to lowest priority.
Complex Form Structures
The bundle supports complex nested form structures, as often found in e-commerce applications:
core_shop_customer_registration:
form_type: App\Form\Type\CustomerRegistrationForm
form_data_resolver: OpenDxp\Bundle\HeadlessBundle\Form\FormDataResolver\DataObjectDataResolver
form_data_resolver_config: { className: 'CoreShopCustomer' }
attributes:
address:
priority: 198
address.street:
priority: 4
# more nested fields
Data Processing and Normalization
The bundle includes a SchemaDataNormalizer that normalizes form data for the API response. This normalizer:
- Correctly processes enum values
- Converts complex objects into simple IDs
- Handles arrays and nested structures
API Endpoint Integration
API Forms can be used in various API endpoints:
api_routes:
customer_create:
route:
path: '/customers'
methods: [ 'POST' ]
action_class: CoreShopHeadlessBundle\Domain\Customer\Action\CreateCustomerAction
action_request:
request_class: CoreShopHeadlessBundle\Domain\Customer\Command\CreateCustomerCommand
request_form:
reference: '#/api_forms/customer_registration'