cyve/json-schema-form-bundle
Install the Bundle
composer require cyve/json-schema-form-bundle
Ensure Cyve\JsonSchemaFormBundle\CyveJsonSchemaFormBundle::class is registered in config/bundles.php.
Define a JSON Schema
Create a schema file (e.g., config/schema/product.json) or define it inline:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Product",
"type": "object",
"properties": {
"name": { "type": "string" },
"price": { "type": "number", "minimum": 0 }
},
"required": ["name"]
}
Generate a Form
use Cyve\JsonSchemaFormBundle\Form\Type\SchemaType;
use Cyve\JsonSchemaFormBundle\Validator\Constraint\Schema;
$schema = json_decode(file_get_contents('config/schema/product.json'));
$form = $this->createForm(SchemaType::class, $product, [
'data_schema' => $schema,
'constraints' => [new Schema($schema)],
]);
Use this bundle to dynamically generate forms for admin panels (e.g., EasyAdmin, SonataAdmin) where schemas are stored in the database or config files. Example:
// In a controller or admin class
$form = $this->createFormBuilder($entity)
->add('dynamic_form', SchemaType::class, [
'data_schema' => $this->getSchemaFromDatabase($entityType),
])
->getForm();
Centralized Schema Management
Store schemas in YAML/JSON files (e.g., config/schemas/) or fetch them dynamically from an API. Use a service to load schemas:
// src/Service/SchemaLoader.php
class SchemaLoader
{
public function load(string $schemaPath): object
{
return json_decode(file_get_contents($schemaPath));
}
}
Reusable Form Types
Extend SchemaType to add custom logic (e.g., default values, custom validation):
use Cyve\JsonSchemaFormBundle\Form\Type\SchemaType as BaseSchemaType;
class CustomSchemaType extends BaseSchemaType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'custom_option' => 'default_value',
]);
}
}
Nested Forms
Handle nested objects/arrays by leveraging the bundle’s automatic SchemaType generation for type: "object" and type: "array":
{
"properties": {
"tags": {
"type": "array",
"items": { "type": "string" }
}
}
}
Renders as a CollectionType with entry_type set to TextType.
Validation Integration
Combine with Symfony’s validation by adding the Schema constraint to the root form:
$form = $this->createForm(SchemaType::class, $data, [
'data_schema' => $schema,
'constraints' => [
new Schema($schema),
new Assert\NotBlank(),
],
]);
Twig Integration Pass the form to Twig and render it like any other Symfony form:
{{ form_start(form) }}
{{ form_widget(form) }}
<div class="help-text">{{ form_help(form) }}</div>
{{ form_end(form) }}
API-Driven Schemas Fetch schemas from an external API (e.g., Swagger/OpenAPI) and cache them:
$schema = json_decode($this->httpClient->request('GET', '/api/schema')->getContent());
Dynamic Schema Updates
Use Symfony’s EventDispatcher to reload schemas when they change (e.g., after a file watcher event):
$dispatcher->addListener('schema.updated', function () {
$this->container->get('schema_loader')->clearCache();
});
Schema Parsing Errors
$schema or unsupported draft versions) throw exceptions during form creation.SchemaType:
use Justinrainbow\JsonSchema\Validator;
$validator = new Validator();
$validator->validate($schema, $schema); // Validate against itself
if (!$validator->isValid()) {
throw new \RuntimeException('Invalid schema');
}
Circular References
$ref references (e.g., items referencing the parent schema) cause infinite recursion.$id and $ref sparingly or resolve references manually before passing to the bundle.Form Type Overrides
SchemaType and override the buildForm method to inject custom types:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('custom_field', CustomType::class);
parent::buildForm($builder, $options);
}
Validation Message Localization
justinrainbow/json-schema) are not translated.# config/validation.yaml
Cyve\JsonSchemaFormBundle\Validator\Constraint\Schema:
message: "The input '{{ propertyPath }}' is invalid."
Inspect Generated Forms Dump the form structure to debug mapping issues:
$form->getConfig()->getFormType()->getName();
$form->getConfig()->getFormType()->getParent()->getName();
Schema Validation Logs
Enable verbose logging for justinrainbow/json-schema:
$validator = new Validator();
$validator->setLogger(new \Monolog\Logger('schema'));
Form Options Overrides
Use form_theme to customize rendering without modifying the schema:
{# templates/form_theme.html.twig #}
{% block schema_string_widget %}
{{ parent() }} <span class="schema-hint">{{ form.vars.data_schema.description }}</span>
{% endblock %}
Custom Form Types
Register new mappings in the bundle’s configuration (config/packages/cyve_json_schema_form.yaml):
cyve_json_schema_form:
form_types:
custom_type: App\Form\Type\CustomType
Schema Preprocessing Use a compiler pass to modify schemas before form generation:
// src/DependencyInjection/Compiler/SchemaCompilerPass.php
public function process(ContainerBuilder $container)
{
$definition = $container->findDefinition('cyve_json_schema_form.schema_processor');
$definition->addMethodCall('addProcessor', [new Reference('app.schema_processor')]);
}
Validation Extensions
Extend the Schema constraint to add custom validation rules:
use Cyve\JsonSchemaFormBundle\Validator\Constraint\Schema as BaseSchema;
class ExtendedSchema extends BaseSchema
{
public function validatedBy()
{
return [get_class(), 'validateCustomRule'];
}
public static function validateCustomRule($value, Constraint $constraint)
{
if ($value === 'forbidden') {
throw new ConstraintViolation(
'Custom rule violated',
[],
[],
$constraint,
'custom_rule'
);
}
}
}
Performance Optimization
$cache = new \Symfony\Component\Cache\SimpleFileCache();
$schema = $cache->get('schema_' . md5($schemaPath), function () use ($schemaPath) {
return json_decode(file_get_contents($schemaPath));
});
How can I help you explore Laravel packages today?