Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Json Schema Form Bundle Laravel Package

destro/json-schema-form-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require cyve/json-schema-form-bundle
    

    Add the bundle to config/bundles.php:

    return [
        // ...
        Cyve\JsonSchemaFormBundle\JsonSchemaFormBundle::class => ['all' => true],
    ];
    
  2. First Use Case: Define a JSON schema (e.g., schemas/product.json):

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "Product",
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "price": { "type": "number" }
      },
      "required": ["name"]
    }
    

    Load and generate a form in a controller:

    use Cyve\JsonSchemaFormBundle\Form\Type\SchemaType;
    use Cyve\JsonSchemaFormBundle\Validator\Constraint\Schema;
    
    public function createForm(Request $request)
    {
        $schema = json_decode(file_get_contents(__DIR__.'/schemas/product.json'));
        $form = $this->createForm(SchemaType::class, new \StdClass(), [
            'data_schema' => $schema,
            'constraints' => [new Schema($schema)],
        ]);
        return $this->renderForm($form);
    }
    
  3. Twig Integration: Use the form_row or form_widget functions in your template:

    {{ form_start(form) }}
        {{ form_row(form) }}
        <button type="submit">Save</button>
    {{ form_end(form) }}
    

Implementation Patterns

Core Workflows

  1. Schema-Driven Form Generation:

    • Store JSON schemas in version-controlled files (e.g., schemas/ directory) for consistency.
    • Dynamically load schemas based on user roles or contexts:
      $schema = json_decode(file_get_contents("schemas/{$role}_schema.json"));
      
  2. Nested Forms: Handle nested objects/arrays by leveraging SchemaType recursively:

    {
      "properties": {
        "address": {
          "type": "object",
          "properties": {
            "street": { "type": "string" }
          }
        }
      }
    }
    

    The bundle automatically generates nested SchemaType forms.

  3. Validation Integration: Use the Schema constraint for validation:

    $form->add('product', SchemaType::class, [
        'data_schema' => $schema,
        'constraints' => [new Schema($schema)],
    ]);
    

    Errors are mapped to JSON schema keywords (e.g., required, type).

  4. Dynamic Schema Loading: Fetch schemas from an API or database:

    $schema = json_decode($apiClient->get('/schemas/product'));
    

Integration Tips

  • Symfony Forms Extensions: Extend the bundle’s SchemaType to add custom logic:

    use Cyve\JsonSchemaFormBundle\Form\Type\SchemaType;
    
    class CustomSchemaType extends SchemaType {
        public function configureOptions(OptionsResolver $resolver) {
            $resolver->setDefaults([
                'custom_option' => 'default_value',
            ]);
        }
    }
    

    Register the extension in services.yaml:

    services:
        App\Form\Type\CustomSchemaType:
            tags: [form.type]
    
  • Event Listeners: Attach listeners to FormEvents for pre/post-processing:

    $form->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $data = $event->getData();
        // Modify data before form rendering
    });
    
  • CSRF and CSRF Protection: Ensure the form includes CSRF tokens:

    {{ form_start(form, {'attr': {'novalidate': 'novalidate'}}) }}
    

Gotchas and Tips

Pitfalls

  1. Schema Validation:

    • The bundle does not validate the JSON schema itself (e.g., syntax errors). Use a library like justinrainbow/json-schema to validate schemas before form generation:
      use Justinrainbow\JsonSchema\Validator;
      
      $validator = new Validator();
      $validation = $validator->validate($schema, (object) ['$ref' => 'http://json-schema.org/draft-07/schema#']);
      if (count($validation->errors())) {
          throw new \RuntimeException("Invalid JSON schema");
      }
      
  2. Circular References:

    • JSON schemas with $ref circular references may cause infinite recursion. Use draft-07 or later and ensure references are resolvable:
      {
        "$ref": "#/definitions/user"
      }
      
  3. Form Options Overrides:

    • Custom form options (e.g., label, attr) in the schema are ignored. Override them explicitly:
      $form->add('name', null, [
          'label' => 'Custom Label',
          'attr' => ['placeholder' => 'Enter name'],
      ]);
      
  4. Type Mismatches:

    • The bundle maps JSON schema types to Symfony types loosely. For example:
      • type: "integer"IntegerType (not NumberType).
      • type: "string" with format: "date"BirthdayType (if using Symfony UX).
    • Customize mappings in a subclass of SchemaType.
  5. Performance:

    • Avoid regenerating forms with large schemas in every request. Cache schemas or forms:
      $schemaCache = new \Symfony\Component\Cache\SimpleFileCache(__DIR__.'/cache');
      $schema = $schemaCache->get('product_schema', function() {
          return json_decode(file_get_contents('schemas/product.json'));
      });
      

Debugging Tips

  1. Schema Dumping: Dump the resolved schema to debug mappings:

    $form = $this->createForm(SchemaType::class, $data, ['data_schema' => $schema]);
    dump($form->getConfig()->getOption('data_schema')); // Inspect the schema
    
  2. Form Type Hierarchy: Use debug:form to inspect the generated form structure:

    php bin/console debug:form your_form_name
    
  3. Validation Errors: Check for validation errors in the Symfony profiler or via:

    $form->isValid();
    $form->getErrors(true); // Recursive errors
    

Extension Points

  1. Custom Form Types: Extend the bundle’s type mapping by overriding the getFormType() method in SchemaType:

    protected function getFormType($type, array $options) {
        if ($type === 'custom-type') {
            return CustomType::class;
        }
        return parent::getFormType($type, $options);
    }
    
  2. Constraint Validation: Add custom validation logic to the Schema constraint:

    use Cyve\JsonSchemaFormBundle\Validator\Constraint\SchemaValidator;
    
    class CustomSchemaValidator extends SchemaValidator {
        public function validateValue($value, Constraint $constraint) {
            if ($value === 'forbidden') {
                $this->context->buildViolation('Value is forbidden')
                    ->atPath('forbidden_field')
                    ->addViolation();
            }
            parent::validateValue($value, $constraint);
        }
    }
    

    Register the validator in services.yaml:

    services:
        App\Validator\Constraints\CustomSchemaValidator:
            tags: [validator.constraint_validator]
    
  3. Schema Resolvers: Implement a custom schema resolver for remote or dynamic schemas:

    use Cyve\JsonSchemaFormBundle\Resolver\SchemaResolverInterface;
    
    class ApiSchemaResolver implements SchemaResolverInterface {
        public function resolve($schema) {
            if (str_starts_with($schema, 'http')) {
                return json_decode(file_get_contents($schema));
            }
            return $schema;
        }
    }
    

    Bind it in services.yaml:

    services:
        App\Resolver\ApiSchemaResolver:
            tags: [json_schema_form.schema_resolver]
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime