startsevdenis/liform-bundle
LiformBundle integrates Liform with Symfony to generate forms directly from JSON Schema. It supports schema-driven form creation, validation, and serialization, reducing manual form configuration for APIs and dynamic Symfony applications.
Read the repository's composer.json and src/ before anything else — with zero stars and zero dependents, the source is the documentation. Identify two things: the bundle class (for Symfony registration) and the service that takes a Symfony\Component\Form\Form (or FormInterface) and returns a JSON Schema array/object.
Important framing for a Laravel developer: this is a Symfony Bundle, not a Laravel package. There is no LaravelServiceProvider, no config/*.php publish tag, and no auto-discovery. You have two realistic paths:
config/bundles.php, then inject the schema-generator service into a controller/API resource.symfony/form alongside it, build FormType classes as plain PHP, and bypass the bundle entirely by instantiating the generator service yourself (or copying the ~single-purpose converter class into your app). The bundle wiring buys you nothing in Laravel; the conversion logic is the only valuable part.Minimal first use case: define one AbstractType with a text field, an email field, and a ChoiceType, convert it to JSON Schema, and POST that array to a react-jsonschema-form or JSON Forms renderer. That closes the loop and tells you immediately whether the output shape matches your frontend library's expectations.
Server-driven forms (the main value proposition). Treat the Symfony FormType as the single source of truth: it defines the fields, types, and constraints; the bundle emits JSON Schema; the frontend renders it dynamically. This removes hand-maintained form definitions in JS and keeps validation rules in one place.
Schema endpoint pattern. Expose a route like GET /api/forms/{name}/schema that returns the generated schema. Cache the result aggressively (route cache or Cache::remember in Laravel) keyed by form class + app version, since generation walks the form tree and is not free.
Constraint-driven contract. Lean on Symfony validation constraints (NotBlank, Length, Email, Regex, Choice) and verify how each maps to JSON Schema keywords (required, minLength/maxLength, format, pattern, enum). Where a constraint doesn't map, add it via a custom form type extension rather than post-processing the array in your controller.
Shared DTO layer. Build FormTypes around your existing DTOs/request objects so the form, the schema, and the server-side validation all derive from one class. In Laravel this means your FormRequest rules still exist for actual validation — the schema is advisory for the client, not enforcement.
Frontend integration tips. Pin the JSON Schema draft your renderer supports and assert it in a test. Keep the schema free of UI hints (ui:*) unless the bundle/your extension emits them — most renderers expect uiSchema as a separate payload.
config publishing, no facade. Any tutorial you find will be Symfony-flavored. Budget time for manual wiring or consider this a "reference implementation to copy" rather than a dependency.composer.json and read every line of the converter before shipping. If you only need 200 lines of behavior, vendoring the class is defensible.CollectionType typically becomes type: array with items; verify item schemas are generated recursively and that min/max entries are honored. RepeatedType and Compound forms are the usual suspects for broken output.ChoiceType handling. Check that choices become enum with the submitted values, not the human labels. This trips people up when choice_label or a choice loader is involved.DataTransformers and mapped entity objects may cause the generator to inspect live objects. Generate schemas from an empty/unmapped form instance where possible to avoid hydration side effects.FormType classes to JSON Schema types. If none exist, subclassing the generator is your escape hatch.label/help text flowing into title/description will be untranslated or in your default locale — decide whether the frontend translates or the backend emits per-locale schemas.How can I help you explore Laravel packages today?