https://github.com/jooservices/form-builder
Standalone PHP 8.5+ schema-driven form builder for JSON-defined forms.
https://github.com/jooservices/form-builder
composer-package form-builder forms html-renderer jooservices json-schema php php85 schema-driven validation
Last synced: 16 days ago
JSON representation
Standalone PHP 8.5+ schema-driven form builder for JSON-defined forms.
- Host: GitHub
- URL: https://github.com/jooservices/form-builder
- Owner: jooservices
- License: mit
- Created: 2026-05-18T11:33:47.000Z (about 1 month ago)
- Default Branch: develop
- Last Pushed: 2026-05-18T14:16:25.000Z (about 1 month ago)
- Last Synced: 2026-05-18T15:21:58.999Z (about 1 month ago)
- Topics: composer-package, form-builder, forms, html-renderer, jooservices, json-schema, php, php85, schema-driven, validation
- Language: PHP
- Size: 169 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# JOOservices Form Builder
[](https://github.com/jooservices/form-builder/actions/workflows/ci.yml)
[](https://php.net)
[](LICENSE)
[](https://packagist.org/packages/jooservices/form-builder)
[](https://codecov.io/gh/jooservices/form-builder)
[](https://scorecard.dev/viewer/?uri=github.com/jooservices/form-builder)
`jooservices/form-builder` is a standalone PHP 8.5+ schema-driven form builder for JSON-defined forms. It parses form schemas, validates schema shape, renders semantic HTML, returns renderless config arrays, exposes asset manifests or safe asset tags, validates submitted values, normalizes submitted values, and supports repeaters, rich-text editor metadata, media input metadata, and generic file input metadata without coupling to Laravel, WordPress, Joomla, Symfony upload objects, PSR-7 uploads, or storage frameworks.
## Install
```bash
composer require jooservices/form-builder
```
## Quick Start
```php
use JOOservices\FormBuilder\FormBuilder;
$builder = FormBuilder::fromJson($json);
$schema = $builder->validateSchema();
$html = $builder->renderHtml([
'values' => $existingValues,
'errors' => $errors,
]);
$config = $builder->toConfigArray();
$assets = $builder->assets();
$assetTags = $builder->renderAssetTags();
$submission = $builder->validateSubmission($input);
if ($submission->passes()) {
$values = $submission->normalizedValues();
}
```
## JSON Schema Example
```json
{
"key": "default_story_form",
"name": "Default Story Form",
"version": "1.0.0",
"method": "post",
"fields": [
{
"key": "title",
"type": "text",
"label": "Title",
"required": true,
"placeholder": "Story title",
"maxlength": 255
},
{
"key": "intro",
"type": "editor",
"label": "Intro",
"required": true,
"html": true,
"editor": {
"provider": "tinymce",
"profile": "basic_html"
}
},
{
"key": "chapters",
"type": "repeater",
"label": "Chapters",
"required": true,
"min_items": 1,
"default_items": 1,
"fields": [
{
"key": "title",
"type": "text",
"label": "Chapter Title",
"required": true
},
{
"key": "media",
"type": "media",
"label": "Chapter Media",
"accept": ["image/*"],
"drag_drop": true,
"preview": true
},
{
"key": "attachment",
"type": "file",
"label": "Chapter Attachment",
"accept": [".pdf", ".docx", "application/pdf"],
"max_files": 1,
"max_size_mb": 20
},
{
"key": "content",
"type": "editor",
"label": "Chapter Content",
"required": true,
"html": true
}
]
}
]
}
```
## Render HTML
Editor fields render as safe `` fallbacks with `data-form-editor`, `data-form-editor-profile`, and `data-form-editor-config`. Media fields render as standard `` controls with optional drag/drop and preview containers driven by data attributes. File fields render as standard `` controls with `data-form-file`, `data-form-file-accept`, `data-form-file-max-files`, and `data-form-file-max-size-mb` metadata, including repeater-safe names such as `chapters[0][documents][]`.
## Render Config
`toConfigArray()` returns a renderless structure for Blade, Vue, React, CLI previews, or custom UI adapters. Repeater items, editor metadata, media metadata, and file metadata are preserved in the config output.
## Assets
Use the asset facade when the consuming application needs standalone CSS/JS metadata without framework coupling:
```php
$builder = FormBuilder::fromJson($json);
$manifest = $builder->assets([
'tinymce_url' => 'https://cdn.tiny.cloud/1/no-api-key/tinymce/7/tinymce.min.js',
]);
echo $builder->renderAssetTags();
```
The manifest exposes `css`, `js`, `inline_css`, `inline_js`, `features`, and `providers`. TinyMCE is opt-in by schema usage and can be disabled or overridden:
```php
$manifest = $builder->assets([
'load_tinymce' => false,
'package_asset_mode' => 'url',
'package_asset_base_url' => 'https://static.example.com/form-builder',
]);
```
If `package_asset_mode` is set to `url` without a usable `package_asset_base_url`, package helper assets are omitted instead of silently falling back to inline output.
Use `renderAssetTags()` for simple PHP apps. In WordPress Management or any host application, prefer reading the manifest and enqueueing or serving those assets in the application layer.
## File Versus Media
- `media` is for media-picker style UX such as image, video, preview, or dropzone flows.
- `file` is for generic upload inputs such as PDF, DOCX, CSV, or ZIP.
- Both stay at schema, HTML, config, validation, and normalization level only.
- Neither field moves, stores, or validates uploaded bytes.
## Validate Submission
Submission validation returns structured `ValidationErrorData` objects with `path`, `message`, `code`, and `context`. Repeater paths follow dot notation such as `chapters.0.title`. File and media validation accept metadata-style values, scalar references, or lists according to field shape, but they do not depend on framework upload classes.
## Repeater Behavior
- Repeater item names render as `chapters[0][title]` and `chapters[0][media][]` for multiple media.
- Default repeater items render when no runtime values exist.
- Submitted order is preserved.
## Custom Fields
Register a custom field adapter when you need application-specific field behavior:
```php
$builder->registerFieldAdapter($adapter);
```
Or use the convenience registration API when you already have custom renderer, validator, and normalizer objects:
```php
$builder->registerFieldType('media_picker', $renderer, $validator, $normalizer);
```
## Security Notes
- Labels, placeholders, text values, and attributes are escaped.
- Event handler attributes like `onclick` are filtered from rendered attributes.
- Editor content is preserved safely inside `` output.
- This package does not sanitize rich HTML content by default.
- This package does not inspect file contents, read upload bytes, or store uploaded files.
- Consuming applications must validate real uploads, MIME policy, file size, and storage server-side.
- Asset tags escape URLs and attributes, but host applications still control CSP, CDN policy, and static file serving.
## What This Package Does Not Do
- It does not save posts, pages, or database records.
- It does not perform WordPress, Joomla, or Laravel integration.
- It does not bundle TinyMCE, CKEditor, TipTap, Quill, or uploader libraries into PHP source.
- It does not move or store uploaded files.
- It does not call `wp_enqueue_script`, service providers, route registries, or framework lifecycle hooks.
- It does not provide AI content generation.
## WordPress Management Guidance
- Store schemas in WordPress Management configuration or database layers.
- Use `FormBuilder` to validate schema, render HTML or config, and validate or normalize submitted values.
- Read `assets()` and enqueue or serve the returned manifest through WordPress Management, not inside this package.
- Handle actual upload transport, storage, attachment creation, and security policy inside WordPress Management.
## Docs And AI Guidance
- [Documentation](docs/README.md)
- [AGENTS.md](AGENTS.md)
- [CLAUDE.md](CLAUDE.md)
- [AI Skills](ai/skills/README.md)