ambelz / json-to-form
Symfony bundle to transform JSON structures into forms
Installs: 35
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
pkg:composer/ambelz/json-to-form
Requires
- php: >=8.2
- symfony/form: ^7.0
- symfony/framework-bundle: ^7.0
- symfony/property-access: ^7.0
- symfony/ux-dropzone: ^2.27
- symfony/ux-live-component: ^2.27
- symfony/validator: ^7.3
Requires (Dev)
- phpunit/phpunit: ^10.0
README
Author: Christophe Abillama christophe.abillama@gmail.com
Symfony bundle to transform JSON structures into forms.
Installation
composer require ambelz/json-to-form
Add the bundle in config/bundles.php:
return [ // ... Ambelz\JsonToFormBundle\JsonFormBundle::class => ['all' => true], ];
Quick Start
For quick usage, you only need 3 elements:
1. 📋 A JSON structure that follows the format
Documentation: For detailed structure documentation and examples, see STRUCTURE.md
File name: my-form.json
Place the JSON file in config/forms/ directory.
{
"slug": "my-simple-form",
"sections": [
{
"slug": "basic-info",
"title": "Basic Information",
"categories": [
{
"slug": "contact",
"title": "Contact",
"questions": [
{
"key": "name",
"type": "text",
"label": "Your Name",
"required": true
}
]
}
]
// Submit button will be default if not specified
// See STRUCTURE.md for submit button configuration
}
]
}
2. 🎛️ A classic controller that loads JSON and renders the main view
<?php declare(strict_types=1); namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Attribute\Route; class IndexController extends AbstractController { private const JSON_FORMS_PATH = __DIR__.'/../../config/forms/'; #[Route('/', name: 'homepage')] public function showForm(): Response { $jsonFile = self::JSON_FORMS_PATH.'my-form.json'; if (!file_exists($jsonFile)) { throw new NotFoundHttpException("Form not found"); } $jsonFileContent = file_get_contents($jsonFile); $jsonStructure = json_decode($jsonFileContent, true); return $this->render('homepage.html.twig', [ 'jsonStructure' => $jsonStructure, ]); } }
3. 🎨 Main template that includes the Live Component
{# templates/homepage.html.twig #} {% extends 'base.html.twig' %} {% block title %}Homepage{% endblock %} {% block body %} <div class="container"> {% for label, messages in app.flashes %} <div class="alert alert-{{ label == 'error' ? 'danger' : label }} alert-dismissible fade show" role="alert"> {% for message in messages %} {{ message }} {% endfor %} <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> {% endfor %} <h1>My form</h1> <!-- LiveComponent for the form --> <div class="mb-5"> {{ component('JsonFormComponent', { jsonStructure: jsonStructure, }) }} </div> </div> {% endblock %}
4. 🎨 Twig Template for the Live Component
{# templates/components/JsonFormComponent.html.twig #} <div {{ attributes }}> {{ form(form, { attr: { 'data-action': 'live#action', 'data-live-action-param': 'save' } }) }} </div>
5. 🎛️ A Live Component to handle the form
<?php declare(strict_types=1); namespace App\Twig\Components; use Ambelz\JsonToFormBundle\Service\JsonToFormTransformer; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormInterface; use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; use Symfony\UX\LiveComponent\Attribute\LiveAction; use Symfony\UX\LiveComponent\Attribute\LiveProp; use Symfony\UX\LiveComponent\ComponentWithFormTrait; use Symfony\UX\LiveComponent\DefaultActionTrait; #[AsLiveComponent('JsonFormComponent')] class JsonFormComponent extends AbstractController { use ComponentWithFormTrait; use DefaultActionTrait; #[LiveProp] public array $jsonStructure; public function __construct( private JsonToFormTransformer $jsonToFormTransformer, ) { } protected function instantiateForm(): FormInterface { $data = []; // initial data to pre-fill the form, in format [sectionSlug][categorySlug][questionKey] = value $builder = $this->createFormBuilder($data, [ 'label_attr' => [ 'class' => 'h2', // class for main label ], 'attr' => [ 'class' => 'p-3', // class for the main form block ], ]); return $this->jsonToFormTransformer->transform($this->jsonStructure, $data, $builder); } #[LiveAction] public function save() { $this->submitForm(); $form = $this->getForm(); if (!$form->isValid()) { $this->addFlash('error', 'The form contains errors. Please correct them.'); return; } // Process form data $data = $form->getData(); // Save to database, send email, etc. $this->addFlash('success', 'Congratulations! Your form has been submitted successfully.'); } }
That's it! 🎉
With these 5 elements, you have a functional dynamic form generated from a JSON structure with live updates.
💡 Note: You can configure submit buttons directly in your JSON structure at the section level with custom labels and CSS classes. If no submit is configured, the bundle automatically adds a default "Submit" button at the end of the form. Manage translations in your own application for maximum flexibility!
Form Data Structure
When the form is submitted and valid, $form->getData() returns an associative array where:
- Keys are the
keyvalues from your JSON questions - Values are the user-submitted values
Example:
$data = $form->getData(); // $data will contain: [ 'sectionSlug' => [ 'categorySlug' => [ 'questionKey' => 'value', ], ], ]
Data processing:
if ($form->isSubmitted() && $form->isValid()) { $data = $form->getData(); // Access individual values $userName = $data['sectionSlug']['categorySlug']['questionKey']; $userEmail = $data['sectionSlug']['categorySlug']['questionKey']; // Save to database, send emails, etc. $user = new User(); $user->setName($userName); $user->setEmail($userEmail); $entityManager->persist($user); $entityManager->flush(); }
Form Structure Documentation
For detailed documentation on form structure, field types, options, constraints, and examples, please refer to the STRUCTURE.md file.