Skip to main content

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:

  1. Form Type: The Symfony Form Type class that defines the form
  2. Form Data Resolver: A service that provides the data for the form
  3. Form Options Resolver: Optional – A service that provides additional options for the form
  4. Validation Groups: Optional – Defines validation groups for the form
  5. 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:

  1. NullDataResolver: Returns null (for new entities)
  2. DataObjectDataResolver: Creates or loads a OpenDXP data object
  3. 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:

  1. Correctly processes enum values
  2. Converts complex objects into simple IDs
  3. 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'