Search by

contenir / formbuilder

peptolab

This package is abandoned and no longer maintained. The author suggests using the contenir/contenir-formbuilder package instead.

Framework-agnostic form-builder engine for Contenir CMS — runtime-editable form definitions, builder-form construction, server-side submission validation, and their storage with php-db on MySQL or SQLite.

Package info

github.com/contenir/contenir-formbuilder

pkg:composer/contenir/formbuilder

Statistics

Installs: 2 172

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.0.1 2026-10-21 13:19 UTC

README

Formerly contenir/formbuilder; the old package is abandoned in favour of this one.

Continuous Integration codecov

Framework-agnostic form-builder engine for Contenir CMS.

It turns a runtime-editable form definition (sections, groups, rows and fields, usually stored in a database) into a working Laminas\Form\Form, validates submissions against it, and renders the markup:

  • Definitions. Immutable value objects describing a form, its layout, notifications and webhooks.
  • Field types. 17 built-in types behind a registry you can extend.
  • Validators. A small curated vocabulary mapped onto Laminas validators.
  • Conditional logic. Show/hide rules evaluated on the server (and mirrored by the client).
  • Submission. CSRF, a honeypot spam trap, conditional gating, optional file uploads through contenir/contenir-storage, and observers (registrars) that persist or forward the result.
  • Rendering. Framework-free HTML for single-page or stepped forms.
  • Merge tags. {field:name}, {form:title}, {entry:fields} and custom namespaces for notification templates and redirect URLs.
  • Storage. The form_* tables as a php-db migration, a form loader, form and entry repositories, a streamed CSV export and retention-based redaction, on MySQL or SQLite.
  • Delivery. Registrars that store entries and send notification emails and signed webhooks, through a delivery seam a host can put a queue behind.

This is the framework-free core, shared by the CMS and the site adapters. contenir/contenir-formbuilder-laminas-mvc wires it into a laminas-mvc application.

Requirements

  • PHP 8.3, 8.4 or 8.5
  • laminas-form 3.20+, laminas-inputfilter, laminas-validator, laminas-filter
  • laminas-session (backs the CSRF token)
  • php-db 0.6 and peptolab/phpdb-migration 0.2, with php-db's MySQL or SQLite driver, for storage
  • ext-curl for webhooks
  • Optional: contenir/contenir-storage for the file field type

Installation

composer require contenir/contenir-formbuilder

The 0.x releases, which support PHP 8.1, remain available from the 0.x branch and v0.* tags; see UPGRADE-2.0.md.

Usage

use Contenir\FormBuilder\Definition\FieldDefinition;
use Contenir\FormBuilder\Definition\FormDefinition;
use Contenir\FormBuilder\Definition\GroupDefinition;
use Contenir\FormBuilder\Definition\RowDefinition;
use Contenir\FormBuilder\Definition\SectionDefinition;
use Contenir\FormBuilder\FieldType\FieldTypeRegistry;
use Contenir\FormBuilder\Registrar\WebhookRegistrar;
use Contenir\FormBuilder\Render\FormMarkup;
use Contenir\FormBuilder\Service\FormBuilderService;
use Contenir\FormBuilder\Service\FormSubmissionService;
use Contenir\FormBuilder\Validator\ValidatorFactory;

$definition = new FormDefinition(
    id: 1,
    slug: 'contact',
    title: 'Contact us',
    submitLabel: 'Send',
    sections: [new SectionDefinition(id: 1, key: 'main', groups: [
        new GroupDefinition(id: 1, rows: [new RowDefinition(id: 1, fields: [
            new FieldDefinition(id: 1, type: 'text', name: 'name', label: 'Name', required: true),
            new FieldDefinition(id: 2, type: 'email', name: 'email', label: 'Email', required: true),
        ])]),
    ])],
);

$builder = new FormBuilderService(new FieldTypeRegistry(), new ValidatorFactory());

// GET: render the form
$form = $builder->build($definition);
echo (new FormMarkup())->render($definition, $form);

// POST: validate and dispatch
$service = new FormSubmissionService($builder);
$service->attach(new WebhookRegistrar($logger));
$result = $service->submit($definition, $_POST, $_FILES, ['ip' => $_SERVER['REMOTE_ADDR'] ?? null]);

if ($result->valid) {
    // $result->values, $result->entryId
} elseif ($result->isSpam) {
    // answer as if it succeeded
} else {
    echo (new FormMarkup())->render($definition, $result->form); // with errors
}

The full public API is described in docs/:

Page Covers
Definitions FormDefinition, sections, groups, rows, fields, validators, notifications, webhooks
Field types The built-in types, FieldTypeRegistry, writing your own type
Validators ValidatorFactory vocabulary and options
Conditional logic RuleEvaluator, ConditionalRulesParser
Building and submitting FormBuilderService, FormSubmissionService, SubmissionResult, BuilderForm, observers, uploads
Rendering FormMarkup, FormContentSanitizer
Merge tags TokenReplacer
Storage The form_* tables and migration, PhpDbFormLoader, the form and entry repositories, CSV export, redaction, status changes
Notifications and delivery StoreSubmissionRegistrar, EmailNotificationRegistrar, DeliveryInterface, sending through a queue
Webhooks WebhookRegistrar

Development

The QA toolchain is contenir/contenir-qa-tools. Mago is a standalone binary, installed separately (brew install mago).

composer check             # everything below
composer cs-check          # mago format --check && mago lint
composer static-analysis   # mago analyze
composer test              # unit suite: no I/O, no session, no network
composer test-integration  # integration suite on SQLite, then MySQL: in-memory session, temp files, a local HTTP server
composer test-coverage     # both suites, clover.xml for Codecov
composer mutation-test     # Infection over both suites (needs Xdebug or PCOV)

The webhook integration tests start PHP's built-in web server on a free loopback port. The database tests run on in-memory SQLite, then on MySQL when CONTENIR_TEST_DB_PLATFORM=mysql (connection settings in CONTENIR_TEST_DB_HOST, _PORT, _NAME, _USER and _PASSWORD; the database defaults to contenir_formbuilder_test). A MySQL that cannot be reached skips those tests.

License

MIT. See LICENSE.