Installation: Add the bundle via Composer:
composer require ansien/form-to-json-bundle
No additional configuration is required—it auto-registers with Symfony’s dependency injection.
First Use Case:
Inject FormTransformerInterface into a controller/service and call transform() on a Symfony FormInterface:
use Ansien\FormToJsonBundle\Transformer\Service\FormTransformerInterface;
public function __construct(private FormTransformerInterface $formTransformer) {}
public function index(): JsonResponse {
$form = $this->createForm(YourType::class);
return new JsonResponse($this->formTransformer->transform($form));
}
Key Files:
src/Transformer/Service/FormTransformer.php (core logic).src/Transformer/Type/ (e.g., TextTypeTransformer, CollectionTypeTransformer).src/Normalizer/ for handling values/errors.Form Creation:
Build your Symfony form as usual (e.g., createForm(YourType::class, $entity)).
The bundle handles nested forms (e.g., CollectionType, FormType) recursively.
Transformation:
$json = $this->formTransformer->transform($form);
Output includes:
schema: Form structure (fields, validation rules, labels).values: Current form data (normalized via ValueNormalizer).errors: Validation errors (normalized via ErrorNormalizer).API Integration:
Return the JSON directly in a JsonResponse or use it to:
Override value/error normalization for specific fields:
// src/Normalizer/CustomValueNormalizer.php
use Ansien\FormToJsonBundle\Normalizer\ValueNormalizerInterface;
class CustomValueNormalizer implements ValueNormalizerInterface {
public function normalize($value, FormView $view): ?string {
if ($view->getName() === 'special_field') {
return strtoupper($value); // Custom logic
}
return $value;
}
}
Register it as a service with the form_to_json_bundle.value_normalizer tag.
Use the exclude option in your form type:
$builder->add('field_to_exclude', TextType::class, [
'form_to_json_bundle' => ['exclude' => true],
]);
Generate schemas for forms dynamically (e.g., for a CMS):
$schema = $this->formTransformer->transform($form)['schema'];
$this->serializer->serialize($schema, 'json');
Use the bundle to expose form schemas in API Platform’s getCollectionOperations() or getItemOperations():
# config/api_platform/resources.yaml
App\Entity\YourEntity:
collectionOperationNames: ['get', 'post']
itemOperationNames: ['get', 'put', 'patch']
attributes:
collection:
operationType: 'get'
method: 'GET'
path: '/schemas/your-entity'
normalization_context:
groups: ['form_schema']
Mock the transformer in tests:
$form = $this->createForm(YourType::class);
$transformer = $this->createMock(FormTransformerInterface::class);
$transformer->method('transform')->willReturn(['schema' => [], 'values' => []]);
Circular References:
EntityType) may cause infinite recursion.max_depth in the transformer options:
$this->formTransformer->transform($form, ['max_depth' => 2]);
ChoiceType Handling:
ChoiceType fields include all choices in the schema, which can bloat JSON.choice_loader to lazy-load choices or filter them:
$builder->add('field', ChoiceType::class, [
'choices' => $this->choiceLoader->loadChoices(),
]);
Translation Domain:
translation_domain. Ensure your translator is configured:
# config/packages/translation.yaml
framework:
translator:
paths: ['%kernel.project_dir%/translations']
default_path: '%kernel.project_dir%/translations'
Deprecated Symfony Features:
FormView changes in Symfony 6+).Inspect Raw FormView:
Dump the raw FormView before transformation to debug:
dump($form->createView());
Enable Debug Mode:
Set FORM_TO_JSON_DEBUG env var to true to log transformer steps:
export FORM_TO_JSON_DEBUG=true
Check Normalizers:
Override ValueNormalizer/ErrorNormalizer to log values:
class DebugValueNormalizer implements ValueNormalizerInterface {
public function normalize($value, FormView $view): ?string {
error_log(sprintf(
'Normalizing %s: %s',
$view->getName(),
print_r($value, true)
));
return $value;
}
}
Custom Type Transformers:
Extend AbstractTypeTransformer for unsupported form types (e.g., Symfony\Component\Form\Extension\Core\Type\CustomType):
class CustomTypeTransformer extends AbstractTypeTransformer {
public function transform(FormView $view): array {
return [
'type' => 'custom',
'data' => $view->vars['custom_data'],
];
}
}
Tag the service:
services:
App\Transformer\CustomTypeTransformer:
tags: { name: form_to_json_bundle.type_transformer }
Modify Schema Structure:
Override the root transformer (FormTransformer) to customize the output format:
class CustomFormTransformer extends FormTransformer {
protected function buildSchema(FormView $view): array {
return [
'id' => $view->getName(),
'fields' => $this->transformChildren($view),
];
}
}
Add Metadata: Attach custom metadata to fields via form options:
$builder->add('field', TextType::class, [
'form_to_json_bundle' => [
'metadata' => ['client_id' => 'unique_id_123'],
],
]);
Access it in your transformer:
$metadata = $view->vars['form_to_json_bundle']['metadata'] ?? [];
Avoid Transforming Large Forms:
For forms with many fields (e.g., >100), use max_depth or exclude unnecessary fields.
Cache Transformers: If generating schemas repeatedly (e.g., in a loop), cache the transformer instance:
$transformer = $container->get(FormTransformerInterface::class);
$cacheKey = md5($form->getName());
if (!$cachedSchema = $cache->get($cacheKey)) {
$cachedSchema = $transformer->transform($form);
$cache->set($cacheKey, $cachedSchema, 3600);
}
Lazy-Load Choices:
For ChoiceType with large datasets, use a ChoiceLoader to fetch choices on-demand:
$builder->add('field', ChoiceType::class, [
'choice_loader' => $this->choiceLoader,
]);
How can I help you explore Laravel packages today?