PHPackages                             w3r-one/json-schema-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. w3r-one/json-schema-bundle

ActiveSymfony-bundle[Utility &amp; Helpers](/categories/utility)

w3r-one/json-schema-bundle
==========================

serialize Symfony Forms into JSON schema

1.2.2(1y ago)37.4k↓10.5%[4 issues](https://github.com/w3r-one/json-schema-bundle/issues)MITPHPPHP &gt;=7.4CI passing

Since Jan 31Pushed 1y ago3 watchersCompare

[ Source](https://github.com/w3r-one/json-schema-bundle)[ Packagist](https://packagist.org/packages/w3r-one/json-schema-bundle)[ RSS](/packages/w3r-one-json-schema-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (11)Versions (14)Used By (0)

JsonSchemaBundle
================

[](#jsonschemabundle)

A bundle to serialize a [Symfony Form](https://github.com/symfony/form) into a [JSON Schema](https://json-schema.org/) (RFC `2020-12`).

Installation
------------

[](#installation)

`$ composer require w3r-one/json-schema-bundle`

If you're not using [Symfony Flex](https://github.com/symfony/flex), you've to register the bundle manually:

```
// config/bundles.php

return [
    // ...
    W3rOne\JsonSchemaBundle\W3rOneJsonSchemaBundle::class => ['all' => true],
];
```

Usage
-----

[](#usage)

```
namespace App\Controller;

use App\Entity\Partner;
use App\Form\PartnerType;
use W3rOne\JsonSchemaBundle\JsonSchema;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class FormController extends AbstractController
{
    public function partnerAdd(JsonSchema $jsonSchema): Response
    {
        $form = $this->createForm(PartnerType::class, new Partner(), ['validation_groups' => ['Default', 'Form-Partner']]);

        return new JsonResponse($jsonSchema($form));
    }
```

View the generated JSON Schema```
{
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"$id": "http://localhost/schemas/partner.json",
	"type": "object",
	"title": "partner",
	"properties": {
		"_token": {
			"type": "string",
			"title": "",
			"writeOnly": true,
			"default": "1996112795cc2bfa7d399fb.1rqGabut308UPJvtLSqXwgrrIXMqdei_M0T3DH53B50.tdzwLf-Atgdddf-qZF3dl127SABsENrMfiCdOAwRXvqBz_4Dz5SMfWMF6A",
			"options": {
				"widget": "hidden",
				"layout": "default"
			}
		},
		"name": {
			"type": "string",
			"title": "Nom",
			"options": {
				"widget": "text",
				"layout": "default",
			}
		},
		"types": {
			"type": "array",
			"title": "Types",
			"options": {
				"widget": "choice",
				"layout": "default",
				"attr": {
					"readonly": true
				},
				"choice": {
					"expanded": true,
					"multiple": true,
					"filterable": true,
					"enumTitles": ["Client", "Fabricant", "Sous-traitant", "Installateur", "Fournisseur", "Concurrent", "Gestionnaire"]
				}
			},
			"items": {
				"type": "string",
				"enum": ["customer", "manufacturer", "subcontractor", "installer", "supplier", "rival", "administrator"]
			}
			"uniqueItems": true
		},
		"address": {
			"type": "object",
			"title": "Adresse",
			"options": {
				"widget": "address",
				"layout": "default"
			},
			"properties": {
				"raw": {
					"type": "string",
					"title": "",
					"writeOnly": true,
					"options": {
						"widget": "text",
						"layout": "default",
						"attr": {
							"maxlength": 255,
							"placeholder": "Tapez une adresse"
						}
					}
				},
				"formatted": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				},
				"coords": {
					"type": "object",
					"title": "",
					"options": {
						"widget": "coords",
						"layout": "default"
					},
					"properties": {
						"lat": {
							"type": "string",
							"title": "",
							"options": {
								"widget": "hidden",
								"layout": "default"
							}
						},
						"lng": {
							"type": "string",
							"title": "",
							"options": {
								"widget": "hidden",
								"layout": "default"
							}
						}
					}
				},
				"nb": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				},
				"street": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				},
				"zipcode": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				},
				"state": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				},
				"city": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				},
				"country": {
					"type": "string",
					"title": "",
					"options": {
						"widget": "hidden",
						"layout": "default"
					}
				}
			}
		},
		"url": {
			"type": "string",
			"title": "Site web",
			"options": {
				"widget": "url",
				"layout": "default"
			}
		},
		"email": {
			"type": "string",
			"title": "Adresse email",
			"options": {
				"widget": "email",
				"layout": "default"
			}
		},
	},
	"required": [],
	"options": {
		"widget": "partner",
		"layout": "default",
		"form": {
			"method": "POST",
			"action": "http://localhost/partner/json_schema",
			"async": true
		}
	}
}
```

Purpose
-------

[](#purpose)

The goal behind this bundle is based on the fact that it is complicated for a modern front-end application to maintain a form component that is not mapped directly on a Symfony FormType.

Most of the time, the front-end component is defining form's props in a static way and if the back-end wants to update the form, we need to work twice, it's error prone and it's not extensible at all.

The main idea is to give the lead to the back-end, provide a JSON schema dynamically that will detail the full component and its related documentation ; the front-end "just" have to display and handle the form on his side.

If the Form is changing or even if it's dynamic based on some roles / scopes / etc., the front-end developer have nothing to change.

It's also allow working with forms directly in Twig and in the same time in a Javascript context.

The business rules are not duplicated and are only handled by the back-end.

This bundle doesn't provide any Front-End component, feel free to choose the stack that feet your needs to build your own Javascript Form.

### Logic

[](#logic)

- Back-End
    - Create your FormType as usual (it can include dynamic fiels, ACL, business rules, FormEvents, etc.)
    - Extend it if needed (through `w3r_one_json_schema` or your own Transformers)
    - Optional: sending it to the view and display/test it directly in Twig
    - Serialize the Form into a JSON Schema and send it to the view
    - Optional: serialize the initial data as well to hydrate your form data
- Front-End
    - Create the main form (`options.form.method` + `options.form.action`)
    - Iterate recursively on all sub properties to create the complete form.
    - Map each child with the correct JS component thanks to `options.widget` (+ `options.layout` if needed)
    - Optional: hydrate each field value with initial data
    - Handle submit on XHR (custom HTTP header `X-Requested-With: XMLHttpRequest` thanks to `options.form.async`) or normally
    - Display errors if any - else a flash message / redirect the user

### Concrete exemple

[](#concrete-exemple)

This example allow to handle a form directly in Twig without XHR AND with async Javascript, feel free to drop completely the twig/not async part.

```
