neophp/formbuilder-package

Dev-only drag-and-drop form builder UI generating FormFactory code from entity metadata

Maintainers

Package info

github.com/NeoPHP-Dev/neo-formbuilder-package

Language:CSS

pkg:composer/neophp/formbuilder-package

Transparency log

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-08-08 03:07 UTC

This package is auto-updated.

Last update: 2026-08-08 03:07:21 UTC


README

A dev-only, drag-and-drop UI for building a NeoPHP Form from an existing entity's ORM metadata. Drag fields onto a canvas, reorder them, tweak which ones are required, click Generate code, and copy the resulting FormFactory snippet into your controller. Nothing is written to disk automatically — this is a code generator you paste from, not a live editor.

The UI is a reusable Twig component: use it at its own standalone route, or embed it in any page of your project — including a neo-admin-package page — via a single macro call.

Structure

formbuilder-package/
├── composer.json
├── README.md
└── src/
    ├── NeoFormBuilderPackage.php
    ├── Controllers/
    │   └── FormBuilderController.php
    ├── Middleware/
    │   └── DevOnlyMiddleware.php
    ├── Service/
    │   ├── EntityScanner.php
    │   ├── EntityFieldExtractor.php
    │   └── FormCodeGenerator.php
    ├── Assets/
    │   ├── css/builder.css
    │   └── js/builder.js
    └── Templates/
        ├── components/
        │   └── FormBuilder.macro.html.twig
        └── pages/
            └── builder.html.twig

Dev-only, by design

Every route this package exposes is protected by DevOnlyMiddleware, which checks Config/app.config.php's environment key. Outside environment: dev, every route returns a blocked response. This tool generates code from your entity structure; it has no reason to ever be reachable on a live site.

Installation

php bin/neo package:require neophp/formbuilder-package --project=MyProject

Register it in the project's Config/app.config.php:

return [
    // ...
    'packages' => [
        \Vendor\NeoPHP\FormBuilderPackage\NeoFormBuilderPackage::class,
    ],
];

No configuration file — there is nothing to customize per project.

Using the standalone page

Visit /_formbuilder/ (only reachable in dev). Pick an entity, drag fields onto the canvas, click Generate code, copy the result from the modal that opens.

Embedding the builder in your own page

The entire UI is a single reusable Twig macro, @FormBuilder/components/FormBuilder.macro.html.twig. Embed it in any page of your project — a custom admin page, a dedicated dev tools section, anywhere — without duplicating any markup, CSS, or JS.

1. Provide the list of entities from your own controller

<?php

declare(strict_types=1);

namespace Neo\Src\MyProject\App\Controllers\NeoAdmin;

use Neo\Core\Controller\AbstractController;
use Neo\Core\Http\Response\Types\Response;
use Neo\Core\Routing\Attribute\MainRoute;
use Neo\Core\Routing\Attribute\Route;
use Neo\Core\Security\Middleware\Attribute\Middleware;
use Vendor\NeoPHP\AdminPackage\Middleware\AdminAuthMiddleware;
use Vendor\NeoPHP\FormBuilderPackage\Service\EntityScanner;

#[MainRoute(path: '/admin/formbuilder', name: 'admin.formbuilder')]
#[Middleware(
    use: AdminAuthMiddleware::class,
    onError: 'block',
    params: ['requiredRole' => 'ROLE_ADMIN'],
)]
final class FormBuilderPageController extends AbstractController
{
    #[Route(path: '/', name: 'index', methods: ['GET'])]
    public function index(EntityScanner $scanner): Response
    {
        return $this->render('pages/admin/formbuilder.html.twig', [
            'entities' => $scanner->scan(),
        ]);
    }
}

EntityScanner is the same service the standalone page uses — it lists every entity under the project's Database/Entity/ and any installed package's database/Entity/. You don't need to build your own scanning logic.

2. Import the macro and render it in your template

{# src/MyProject/Templates/pages/admin/formbuilder.html.twig #}
{% extends '@NeoAdmin/layouts/admin_layout.html.twig' %}
{% import '@FormBuilder/components/FormBuilder.macro.html.twig' as FormBuilder %}

{% block stylesheets %}
    <link rel="stylesheet" href="/packages-assets/FormBuilder/css/builder.css">
{% endblock %}

{% block content %}
    {{ FormBuilder.render(entities) }}
{% endblock %}

{% block javascripts %}
    <script src="/packages-assets/FormBuilder/js/builder.js"></script>
{% endblock %}

That's the whole integration — three includes (macro import, CSS link, JS script) plus one line to render it. This works the same whether the host page extends neo-admin-package's layout, your own project layout, or no layout at all.

3. FormBuilder.render(entities, instanceId)

Parameter Default Purpose
entities (required) List of entity class-strings, from EntityScanner::scan()
instanceId 'fb' Prefix for internal element IDs — change it if you ever render the component more than once on the same page, to avoid ID collisions
{{ FormBuilder.render(entities, 'my-custom-id') }}

4. Registering it in the admin sidebar (optional)

If embedding it inside neo-admin-package, add it to admin-system.config.php like any other page:

'sidebar' => [
    // ...
    'formbuilder' => [
        'controller' => \Neo\Src\MyProject\App\Controllers\NeoAdmin\FormBuilderPageController::class,
        'icon' => 'layout-dashboard',
        'title' => 'Form Builder',
    ],
],

Theming

The component never hardcodes colors — every visual value is a CSS custom property scoped to .fb-layout, with a light theme applied by default. Override any of them on an ancestor element (or directly on the component's root, via its id="{instanceId}-root") to match your page:

#my-custom-id-root {
    --fb-bg: #161923;
    --fb-bg-alt: #1b2030;
    --fb-bg-raised: #1e2436;
    --fb-border: #2d3342;
    --fb-text: #e5e7eb;
    --fb-text-muted: #9ca3af;
    --fb-text-faint: #4b5563;
    --fb-accent: #818cf8;
    --fb-accent-hover: #6366f1;
    --fb-accent-soft: rgba(129, 140, 248, 0.15);
    --fb-danger: #f87171;
}

Only the accent color usually needs overriding to match a host page — as done in the neo-admin-package example above (--fb-accent set to the admin panel's own indigo).

How fields are discovered

EntityFieldExtractor reads the entity's ClassMetaData (fieldMappings), the same metadata NeoPHP's ORM uses internally — no separate configuration or annotation is needed beyond the entity's existing #[Column] attributes. The identifier field (#[Id]) is always excluded from the available field list.

ORM types are mapped to form field types as follows:

ORM type Form type
integer, float number
boolean checkbox
datetime datetime
text, json textarea
everything else text

Relations (#[ManyToOne], etc.) are not currently supported — only scalar column fields are listed. Enums are treated as plain text, not as a select with the enum's cases — add relation/enum handling to the generated code by hand.

What this package does not do

  • It never writes files — the code is generated in-browser and copied manually, on purpose, so nothing changes on disk without you reviewing it first.
  • It does not persist your form layout between sessions — reload the page and you start from an empty canvas.
  • It does not validate the generated code compiles — always review it before pasting into a real controller.

License

MIT