effiana/jsonform
Serialize Symfony Forms into JSON Schema for documentation, validation, and client-side form generation. Map form field types to transformers via a resolver, then transform a Form into a JSON schema usable with tools like json-editor or liform-react.
Installation
composer require effiana/jsonform
Add to composer.json if not auto-loaded:
"autoload": {
"psr-4": {
"App\\": "app/",
"Effiana\\JsonForm\\": "vendor/effiana/jsonform/src/"
}
}
Run composer dump-autoload.
First Use Case
Convert a Symfony FormBuilder to JSON Schema:
use Effiana\JsonForm\JsonForm;
use Symfony\Component\Form\FormBuilderInterface;
$form = $this->createFormBuilder()
->add('name', TextType::class)
->add('email', EmailType::class)
->getForm();
$jsonSchema = (new JsonForm())->transform($form);
Where to Look First
JsonForm (main transformer).TypeMapper (handles Symfony field types → JSON Schema).Tests for edge cases (e.g., nested forms, custom types).Define Symfony Form
$form = $this->createFormBuilder()
->add('user', EntityType::class, [
'class' => User::class,
'property' => 'name',
'label' => 'Select User',
])
->add('active', ChoiceType::class, [
'choices' => [true => 'Yes', false => 'No'],
])
->getForm();
Transform to JSON Schema
$schema = (new JsonForm())->transform($form);
// Outputs:
// {
// "type": "object",
// "properties": {
// "user": { "type": "string" },
// "active": { "type": "boolean" }
// }
// }
Extend for Custom Types
Override TypeMapper for unsupported fields (e.g., DateTimeType):
$mapper = new TypeMapper();
$mapper->addType('datetime', function() {
return ['type' => 'string', 'format' => 'date-time'];
});
$jsonForm = new JsonForm($mapper);
Integration with API Platform
Use in a SerializerContextBuilder to dynamically generate schemas:
$contextBuilder->getContext()->addExtraAttributes([
'json_schema' => $jsonForm->transform($form),
]);
Nested Forms: Automatically handles CollectionType/FormType:
->add('tags', CollectionType::class, [
'entry_type' => TextType::class,
'allow_add' => true,
])
Outputs an array schema with items and additionalItems.
Validation Rules: Map Symfony constraints to JSON Schema keywords:
->add('age', IntegerType::class, [
'constraints' => [new Assert\GreaterThan(18)],
])
Outputs:
{ "type": "integer", "minimum": 19 }
Dynamic Forms: Regenerate schema on-the-fly in controllers:
public function edit(Request $request, FormInterface $form) {
$schema = (new JsonForm())->transform($form);
return response()->json(['schema' => $schema]);
}
Outdated Symfony Support
Assert\* constraints may break).TypeMapper or patch the library (MIT license allows modifications).Missing Type Mappings
FileType, JsonType).TypeMapper:
$mapper->addType('json', function() {
return ['type' => 'object'];
});
Nested Form Quirks
CollectionType with prototype may not serialize correctly.var_dump($jsonForm->getSchema()) to inspect intermediate steps.Constraint Conflicts
Length + NotBlank) may generate conflicting schemas.TypeMapper.$jsonForm = new JsonForm();
$jsonForm->setDebug(true); // Logs unsupported types
$schema = $jsonForm->transform($form);
file_put_contents('schema.json', json_encode($schema, JSON_PRETTY_PRINT));
EntityType).Custom Schema Generators
Extend JsonForm to add metadata:
class CustomJsonForm extends JsonForm {
public function transform(FormInterface $form) {
$schema = parent::transform($form);
$schema['$metadata'] = ['source' => 'api'];
return $schema;
}
}
Frontend Integration
{
"schema": { /* JSON Schema */ },
"uiSchema": { /* Custom UI hints */ }
}
Performance
$cache = new \Symfony\Component\Cache\SimpleFileCache();
$schema = $cache->get('form_schema', function() use ($form) {
return (new JsonForm())->transform($form);
});
Testing
FormInterface for unit tests:
$form = $this->createMock(FormInterface::class);
$form->method('getConfig')->willReturn(new FormConfig());
How can I help you explore Laravel packages today?