asimnet / schemava
Visual multilingual form builder built specifically for FilamentPHP panels with SurveyJS-compatible JSON export.
Requires
- php: ^8.3
- filament/filament: ^5.0
- illuminate/contracts: ^11.0|^12.0
- spatie/laravel-package-tools: ^1.15
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/ai: ^0.8.1
- laravel/pint: ^1.29
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0
Suggests
- laravel/ai: Required for the bundled AI assistant adapter (LaravelAiAssistant). Install ^0.8 and configure a provider key to enable AI-assisted form building.
Provides
None
Conflicts
None
Replaces
None
README
Author guide (Arabic): دليل منشئ النماذج المرئي — canvas authoring, containers, conditional logic, quizzes, and preview.
A free, open-source (MIT), visual, multilingual form builder built specifically for FilamentPHP panels — exports SurveyJS-compatible JSON.
Schemava adds a drag-and-drop form designer to your FilamentPHP panel. You build forms visually, store the
schemas in your own Laravel database as plain SurveyJS-compatible JSON, and render them anywhere a survey-core
engine runs (web or mobile). First-class internationalization with automatic RTL/LTR.
Schemava does not bundle SurveyJS Creator. It is an independent builder that produces SurveyJS-compatible
JSON and previews/renders with the MIT-licensed open-source survey-core engine.
Features
- Visual drag-and-drop builder (13 editable field types; advanced SurveyJS types are imported and preserved untouched)
- Multilingual schemas in any locale, with automatic RTL/LTR
- SurveyJS-compatible JSON — no proprietary format, no lock-in
- Conditional logic & branching (a visual rule builder that compiles to
visibleIf/enableIf) - Quiz scoring, file upload, and signature fields
- An optional AI assistant (bring your own provider)
- Five host-overridable contracts (runtime context, access rules, submissions, URLs, AI)
- Multi-tenant-ready: models, table names, storage disk, renderer version, and routes are all configurable
Requirements
- PHP 8.2+ (matching Filament's own floor; the package uses no 8.3-only features)
- Laravel 11 or 12
- FilamentPHP 5
Installation
Schemava is free and open source, and available on Packagist:
composer require asimnet/schemava
Publish and run the migrations (this creates the schemava_schemas and schemava_results tables — the
migrations are publish-only so you can review or adapt them, e.g. custom table names), then publish the builder
assets:
php artisan vendor:publish --tag=schemava-migrations php artisan migrate php artisan filament:assets
Or let the installer do all of that (config + migrations) for you:
php artisan schemava:install php artisan migrate php artisan filament:assets
Nothing is exposed to the internet by installing the package. The public fill-and-submit form and the JSON API are disabled by default — see Collecting responses to opt in deliberately.
Quick start
Register the plugin on your Filament panel provider (e.g. app/Providers/Filament/AdminPanelProvider.php):
use AsimNet\Schemava\SchemavaPlugin; use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... ->plugin(SchemavaPlugin::make()); }
That's the whole integration. Reload your panel and you'll have a Forms section: create a form, then click Design to open the visual builder. No routes or resources to wire up yourself.
What you get out of the box
Registering the plugin provides:
- A Forms Filament resource — list, create, edit, delete (backed by the
SurveySchemamodel). Creating a form drops you straight into the builder. - The visual designer — an asset-bundled page reached from the resource's Design action.
- The designer routes (
survey-designer.*), registered for you from config. - The compiled builder assets, registered with Filament's asset manager and loaded only on the designer screen, so they never bloat the rest of your panel.
The service provider also auto-loads the package's migrations, translations, config, and the route-model
binding for {surveySchema}.
Plugin options
SchemavaPlugin::make() // Register your OWN resource instead of the bundled one (see "Using your own resource"). ->surveyResource(false) // Group the Forms resource in the panel nav, and order it. ->navigationGroup('Content') ->navigationSort(20) // Gate who can see AND open the builder. Return a bool, or a closure returning one. // Defaults to visible for every panel user. ->authorize(fn (): bool => auth()->user()->can('manage-forms')) // Where the designer's "back" control returns to. Without it, the control // falls back to the bundled resource's view page, or to browser history. ->backUrl(fn (SurveySchema $schema): string => FormResource::getUrl('index'));
| Method | Default | What it does |
|---|---|---|
surveyResource(bool) |
true |
Register the bundled Forms resource. Set false to use your own. |
navigationGroup(?string) |
null |
Navigation group for the resource. |
navigationSort(int) |
100 |
Navigation sort order. |
authorize(Closure|bool) |
true |
Gates the resource's canAccess() and nav visibility. |
backUrl(string|Closure|null) |
null |
The designer's "back" destination. routes.designer.back_url (config) wins over it. When neither is set, the control goes back through browser history (to whichever page opened the designer); in a tab with no history it goes to the bundled resource's view page, and hides when there is none. |
Configuration
php artisan vendor:publish --tag=schemava-config
Everything in config/schemava.php:
| Key | Purpose |
|---|---|
models.schema, models.result |
Swap in your own Eloquent subclasses to add domain behaviour (relations, scopes, targeting). The bundled resource, designer, and routes all use whatever you set here. |
tables.schemas, tables.results |
Override the table names if they clash with yours. |
storage.disk |
Filesystem disk for builder images and respondent uploads (default public; use a private/S3 disk in production). |
storage.images_path, storage.files_path |
Path prefixes within that disk. |
renderer_version |
The survey-core version the builder validates and previews against (default 2.3.16). |
routes.enabled |
Whether the package registers the designer routes. Set false if you register your own. |
routes.designer |
Prefix + middleware for the designer routes, and back_url — the designer's "back" destination (see Plugin options). |
designer_ui_locale |
Default designer interface language: 'ar' (default), 'en', or null to follow the app locale. Independent of the browser language; authors can switch it from the designer's "⋯" menu (remembered per browser). |
routes.public |
The optional public form route (disabled by default) — enabled flag, prefix, middleware, name. |
routes.api |
The optional JSON API (disabled by default) — enabled flag, prefix, middleware. |
ai.enabled |
The designer's AI assistant (disabled by default); needs laravel/ai + provider credentials. |
contracts.* |
The five host-overridable implementations — see Extending Schemava. |
navigation.group, navigation.sort |
Defaults for the resource nav (the plugin's navigationGroup() / navigationSort() take precedence). |
policy |
An optional policy class to register for the schema model. Leave null to rely on your own gates. |
Rendering forms to your users
Each form is stored as SurveyJS-compatible JSON on the SurveySchema model's survey_json column. Render
it to your end users with the open-source survey-core engine —
the same one the builder previews with. Pass $schema->survey_json to a survey-core model on the client and
handle its onComplete to submit answers back to your app.
Submissions are persisted as SurveyResult records through the SurveySubmissionHandler contract, so you can
store or route them however your domain needs. Quiz-style scoring is available via the bundled
SurveyScoreCalculator.
Before persisting, the public form and JSON API run submitted answers through the SurveyAnswerValidator
contract, which rejects (422) unknown keys, missing unconditionally-required answers, out-of-list choices
values, and oversized/deeply-nested payloads — see answer_validation in config/schemava.php for the size
caps and Extending Schemava below to swap in your own validator.
Renderer lockstep: set
renderer_versionto the exactsurvey-coreversion every client renders with (web, mobile, …) so the builder always validates against the engine that ultimately renders the form.
Collecting responses (public form + JSON API)
Beyond rendering it yourself, Schemava ships two turnkey ways to collect responses. Both are disabled by
default — they are unauthenticated, internet-facing endpoints, so exposing them is an explicit opt-in. Both
reuse the same access rules (SurveyAccessResolver) and persistence (SurveySubmissionHandler).
Public web form — a standalone fill-and-submit page at GET {prefix}/{hash} (default /surveys/{hash}),
rendered with survey-js-ui and pinned to your renderer_version.
While the public form is disabled, every share affordance hides itself automatically — the designer's
copy-link action, the resource's "Public form URL" entry, and the view page's "Open public form" action —
because the default SurveyUrlResolver returns no public URL rather than a link that would 404.
JSON API for mobile / external clients (default prefix /api/surveys):
| Method & path | Purpose |
|---|---|
GET /api/surveys |
List active forms (id, hash, title, description). |
GET /api/surveys/{hash} |
Fetch one form: survey_json, renderer_version, can_submit. |
POST /api/surveys/{hash}/responses |
Submit { "data": { … } }; returns the created response. |
Who may submit is your decision
Nothing is exposed by accident — both channels ship off, and you enable them in config/schemava.php
(publish it first with php artisan vendor:publish --tag=schemava-config) or via .env:
'routes' => [ 'public' => [ 'enabled' => env('SCHEMAVA_PUBLIC_FORM_ENABLED', false), // true → expose the public form 'middleware' => ['web'], // add 'auth' to require a logged-in user ], 'api' => [ 'enabled' => env('SCHEMAVA_API_ENABLED', false), // true → expose the JSON API 'middleware' => ['api'], // add 'auth:sanctum' / 'auth:api' to require a token ], ],
- When enabled, the default middleware allows guests — that is what a public form is for. Add auth middleware to require a login/token instead.
- For per-form, role, age, or open/close-window rules, bind your own
SurveyAccessResolver— it gates the form and the API identically. - Authenticated submissions are attributed to the current user; guest submissions are stored with no user.
Extending Schemava (contracts)
Schemava resolves six contracts you can replace to integrate it with your domain. Each ships a sensible
default, so you override only what you need — bind your class in config('schemava.contracts.*'):
| Contract | Override to… | Default |
|---|---|---|
RuntimeContextProvider |
feed dynamic values (signed-in user, locale, custom variables) into form piping | DefaultRuntimeContextProvider |
SurveyAccessResolver |
decide who may view/submit a form (roles, age, gender, targeting) | OpenSurveyAccessResolver |
SurveySubmissionHandler |
change how a submission is stored or routed | DefaultSurveySubmissionHandler (creates a SurveyResult) |
SurveyAnswerValidator |
change how submitted answers are checked against the schema before storage | DefaultSurveyAnswerValidator |
SurveyUrlResolver |
build the public/admin URLs for a form | DefaultSurveyUrlResolver |
AiAssistant |
answer builder AI queries | NullAiAssistant (AI disabled) |
// config/schemava.php 'contracts' => [ 'runtime_context' => \App\Surveys\MyRuntimeContext::class, 'access_resolver' => \App\Surveys\MyAccessResolver::class, 'submission_handler' => \AsimNet\Schemava\Support\DefaultSurveySubmissionHandler::class, 'answer_validator' => \AsimNet\Schemava\Support\DefaultSurveyAnswerValidator::class, 'url_resolver' => \AsimNet\Schemava\Support\DefaultSurveyUrlResolver::class, 'ai_assistant' => \App\Surveys\MyAiAssistant::class, ],
AI assistant (optional)
The AI assistant lets editors describe a form in plain language and have it drafted (or rewrite a selected
field). It is off by default (ai.enabled => false). To enable it, install the AI SDK, configure a
provider key, and set SCHEMAVA_AI_ENABLED=true:
composer require laravel/ai
You are responsible for reviewing AI-generated content before use.
Using your own resource
Prefer to build your own Filament resource (custom columns, capability gating, exports)? Disable the bundled one and point your resource at Schemava's model:
->plugin(SchemavaPlugin::make()->surveyResource(false))
use AsimNet\Schemava\Models\SurveySchema; // or your configured subclass use Filament\Actions\Action; use Filament\Facades\Filament; use Filament\Support\Icons\Heroicon; class FormResource extends Resource { protected static ?string $model = SurveySchema::class; // ...your form() and table()... // Add a row/header action that opens the visual builder. The optional // `panel` parameter tells the (panel-agnostic) designer which panel it was // opened from, so plugin options like backUrl() resolve against that panel — // it only matters when more than one panel registers the plugin. Action::make('design') ->icon(Heroicon::PencilSquare) ->url(fn (SurveySchema $record): string => route('survey-designer.show', [ 'surveySchema' => $record, 'panel' => Filament::getCurrentPanel()?->getId(), ])); }
The designer routes stay registered by the package (unless you disable them — see below).
Multi-tenant / custom routes
If your app registers its own designer routes (multi-tenant apps often scope them per tenant), set
routes.enabled to false so the package doesn't register them too, then register your own group pointing at
the package controller:
// config/schemava.php 'routes' => ['enabled' => false],
use AsimNet\Schemava\Http\Controllers\SurveyDesignerController; Route::middleware(['web', 'auth'])->prefix('survey-designer')->name('survey-designer.')->group(function () { Route::get('/{surveySchema}', [SurveyDesignerController::class, 'show'])->name('show'); Route::post('/{surveySchema}/save', [SurveyDesignerController::class, 'save'])->name('save'); Route::post('/upload-image', [SurveyDesignerController::class, 'uploadImage'])->name('upload-image'); Route::post('/{surveySchema}/ai/query', [SurveyDesignerController::class, 'queryAi'])->name('ai-query'); });
Keep the route names (survey-designer.*) — the controller and the bundled resource reference them.
Authorization
The designer controller authorizes the update ability on the schema model only when a policy is registered
for it. With no policy, the routes are protected by their middleware (auth) alone — fine for a single-admin
panel, but not enough when your app has non-admin authenticated users (customers, applicants, members):
any of them could open survey-designer/{id} and rewrite a form. In that case register a policy (Laravel
auto-discovery, your AuthServiceProvider, or config('schemava.policy')) or add a role/permission middleware
to routes.designer.middleware. The bundled resource's own visibility is controlled separately by the plugin's
authorize() option.
Updating
composer update asimnet/schemava php artisan filament:assets
Because you require "asimnet/schemava": "^1.0", Composer's prefer-stable keeps pre-releases off your servers
unless you explicitly opt in.
Troubleshooting
composerstill fails or installs an old version after switching to Packagist: remove any custom repository you added for Schemava earlier, then update:composer config --unset repositories.schemava && composer update asimnet/schemava- The builder doesn't load / assets are missing: run
php artisan filament:assetsafter install/update and make sure it runs as part of your deploy; then hard-refresh. - Image/file uploads fail: check
storage.diskis writable and, if public, runphp artisan storage:link.
FilamentPHP & SurveyJS compatibility
Schemava is built specifically for FilamentPHP panels — not a generic Laravel admin package, and not for
Nova, Orchid, Backpack, MoonShine, or custom panels. It exports SurveyJS-compatible JSON and may render with
the MIT-licensed survey-core engine; it does not include SurveyJS Creator, PDF Generator, or Dashboard.
See THIRD_PARTY_NOTICES.md.
License
Schemava is open-source software licensed under the MIT license. See
THIRD_PARTY_NOTICES.md for the MIT-licensed components it interoperates with (such as
the survey-core renderer).
Trademark notice
Schemava is an independent product and is not affiliated with, endorsed by, or sponsored by FilamentPHP, SurveyJS, or Devsoft Baltic OÜ. FilamentPHP and SurveyJS are trademarks of their respective owners.
Releasing (maintainers)
Publishing is automatic: Composer reads releases straight from this repository's tags, so pushing a tag is all it takes. Releases follow semver — the suffix decides the channel.
# Stable release — users on "^1.0" receive it on their next `composer update`. git tag v1.1.2 && git push origin main v1.1.2 # Beta / pre-release — only consumers who opt in with @beta receive it. git tag v1.2.0-beta.1 && git push origin v1.2.0-beta.1
Run npm run build and commit resources/dist/ before tagging only if the form builder (Vue) changed —
PHP-only changes don't need a rebuild.