PHPackages                             kavalhub/form-generator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. kavalhub/form-generator

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

kavalhub/form-generator
=======================

Easily create, validate, and display forms.

3.3.1(1mo ago)024MITPHPPHP ^8.2CI passing

Since Mar 29Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/kavalhub/form-generator)[ Packagist](https://packagist.org/packages/kavalhub/form-generator)[ Docs](https://github.com/kavalhub/form-generator.git)[ RSS](/packages/kavalhub-form-generator/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (11)Versions (22)Used By (0)

form-generator
==============

[](#form-generator)

PHP-библиотека для программного создания HTML-форм, привязки данных из запроса, валидации и рендеринга с опциональным Bootstrap-декоратором.

Требования
----------

[](#требования)

- PHP ^8.2
- Composer

Установка
---------

[](#установка)

```
composer require kavalhub/form-generator
```

### Laravel (опционально)

[](#laravel-опционально)

```
composer require kavalhub/form-generator-laravel
```

Быстрый старт
-------------

[](#быстрый-старт)

```
use Kavalhub\FormGenerator\Html\Form;
use Kavalhub\FormGenerator\Html\InputSubmit;
use Kavalhub\FormGenerator\Html\InputText;
use Kavalhub\FormGenerator\Request\ElementRequest;
use Kavalhub\FormGenerator\Validator\ElementValidator;
use Kavalhub\FormGenerator\Validator\Interface\ElementValidatorInterface;

$form = (new Form('contact'))
    ->addElement(
        (new InputText('email'))->setRequired()->setPlaceholder('Email')
    )
    ->addElement(
        (new InputSubmit('send'))->setDefaultValue('Отправить')
    );

/** @var ElementValidatorInterface $validator */
$validator = new ElementValidator(new ElementRequest());
$submit = $form->getByName('send');

if ($validator->checkSubmit($submit) && $validator->handle($form)) {
    // данные валидны
}

echo $form->render();
```

Точки расширения
----------------

[](#точки-расширения)

Библиотека построена на интерфейсах — реализации можно подменять:

ИнтерфейсНазначениеРеализации в пакете[`RequestInterface`](src/Request/Interface/RequestInterface.php)Источник данных формы`ElementRequest`, `ArrayRequest`, `PostOnlyRequest`[`ElementValidatorInterface`](src/Validator/Interface/ElementValidatorInterface.php)Валидация и bind`ElementValidator`[`DecoratorInterface`](src/Decorator/Interface/DecoratorInterface.php)Рендеринг с темой[`AbstractDecorator`](src/Decorator/AbstractDecorator.php); Bootstrap — пакет [`form-generator-bootstrap`](packages/bootstrap/)[`AjaxRenderStrategyInterface`](src/Ajax/Interface/AjaxRenderStrategyInterface.php)HTML/CSS для AJAX-патчей`NullAjaxRenderStrategy`; Bootstrap — в bootstrap-пакете[`ElementEventDispatcher`](src/Event/ElementEventDispatcher.php)События элементов (связанные select, фильтры)`ElementChangedEvent`, слушатели в приложенииПодробнее: [docs/element-events.md](docs/element-events.md), [docs/custom-templates.md](docs/custom-templates.md).

 ```
flowchart LR
    App[Приложение] --> RequestInterface
    App --> ElementValidatorInterface
    App --> DecoratorInterface
    App --> AjaxRenderStrategyInterface
    RequestInterface --> ElementRequest
    RequestInterface --> PostOnlyRequest
    RequestInterface --> LaravelRequestAdapter
    ElementValidatorInterface --> ElementValidator
    ElementValidatorInterface --> LaravelElementValidator
    DecoratorInterface --> AbstractDecorator
    AjaxRenderStrategyInterface --> NullAjaxRenderStrategy
    DecoratorInterface --> BootstrapPackage[form-generator-bootstrap]
    AjaxRenderStrategyInterface --> BootstrapPackage
```

      Loading Request: GET, POST и свои адаптеры
----------------------------------

[](#request-get-post-и-свои-адаптеры)

### `ElementRequest` — по умолчанию (`$_REQUEST`)

[](#elementrequest--по-умолчанию-_request)

Читает **GET + POST + cookies**. Подходит для фильтров и форм, отправляемых GET-запросом (см. demo-проект `kavalhub/form-demo`, каталог `src/`).

Demo-приложение
---------------

[](#demo-приложение)

Примеры использования (`Kavalhub\Example\`) вынесены в отдельный проект и **не входят** в autoload при `composer require kavalhub/form-generator`. В репозитории библиотеки каталог `example/` доступен только через `autoload-dev` для PHPUnit.

```
$request = new ElementRequest();
```

### `PostOnlyRequest` — только POST

[](#postonlyrequest--только-post)

```
use Kavalhub\FormGenerator\Request\PostOnlyRequest;

$request = new PostOnlyRequest();
```

### `ArrayRequest` — для тестов и API

[](#arrayrequest--для-тестов-и-api)

```
use Kavalhub\FormGenerator\Request\ArrayRequest;

$request = new ArrayRequest(['contact_email' => 'a@b.c']);
```

### Свой адаптер

[](#свой-адаптер)

Реализуйте `RequestInterface::get(string $name): ?array` — метод возвращает массив значений для поля с данным именем (`getFormName()`).

Validator
---------

[](#validator)

Контракт [`ElementValidatorInterface`](src/Validator/Interface/ElementValidatorInterface.php):

- `checkSubmit(InputSubmit $submit): bool` — была ли отправлена форма
- `handle(ElementInterface $element): bool` — bind из request, required, callbacks, CSRF
- `isValid(): ?bool` — результат последней проверки

Внедряйте интерфейс, а не конкретный класс:

```
public function __construct(private readonly ElementValidatorInterface $validator) {}
```

### Callback-валидаторы

[](#callback-валидаторы)

```
$input->addCallbackValidator(function (InputText $el): bool {
    if (!str_contains($el->getValue(), '@')) {
        $el->addError(['Некорректный email']);
        return false;
    }
    return true;
});
```

Laravel-интеграция
------------------

[](#laravel-интеграция)

Пакет [`kavalhub/form-generator-laravel`](packages/laravel/) — гибридный валидатор:

1. **Core** (`ElementValidator`) — bind, required, callbacks, CSRF
2. **Laravel** (`illuminate/validation`) — правила `required|email` и т.д.

```
use Illuminate\Validation\Factory;
use Kavalhub\FormGenerator\Html\Form;
use Kavalhub\FormGenerator\Html\InputText;
use Kavalhub\FormGenerator\Laravel\LaravelElementValidator;
use Kavalhub\FormGenerator\Laravel\LaravelRequestAdapter;

$request = new LaravelRequestAdapter($illuminateRequest);
$validator = new LaravelElementValidator($request, app(Factory::class));
$validator->setRules([
    'contact_email' => 'required|email',
]);

$form = (new Form('contact'))->addElement((new InputText('email'))->setRequired());

if ($validator->handle($form)) {
    // OK
}
```

Ошибки Laravel автоматически попадают в `addError()` элементов через [`ElementDataCollector`](src/Util/ElementDataCollector.php).

Bootstrap-декоратор
-------------------

[](#bootstrap-декоратор)

Пакет [`kavalhub/form-generator-bootstrap`](packages/bootstrap/) (с 3.3 вынесен из core):

```
composer require kavalhub/form-generator-bootstrap
```

```
use Kavalhub\FormGenerator\Bootstrap\BootstrapDecorator;
use Kavalhub\FormGenerator\Decorator\Interface\DecoratorInterface;

/** @var DecoratorInterface $decorator */
$decorator = new BootstrapDecorator($form);
echo $decorator->getHtml();
```

Кастомные шаблоны для дизайнеров: [docs/custom-templates.md](docs/custom-templates.md).

Blade-декоратор
---------------

[](#blade-декоратор)

Пакет [`kavalhub/form-generator-blade`](packages/blade/) — те же Bootstrap-стили, шаблоны `{ClassName}.php` в каталоге `resources/Blade/`:

```
composer require kavalhub/form-generator-blade
```

```
use Kavalhub\FormGenerator\Blade\BladeDecorator;

echo (new BladeDecorator($form))
    ->setTemplate(__DIR__ . '/resources/form-templates')
    ->getHtml();
```

Для AJAX: `BladeAjaxRenderStrategy`. Demo поддерживает переключатель HTML / Bootstrap / Blade и per-element шаблон для фасета «Бренд».

CSRF-защита (opt-in)
--------------------

[](#csrf-защита-opt-in)

```
$form = (new Form('secure'))
    ->enableCsrf()
    ->addElement(/* ... */);
```

AJAX (3.1+)
-----------

[](#ajax-31)

Библиотека не навязывает JS-фреймворк. Сервер возвращает JSON с ключом `REPLACE` — массив патчей DOM. Два режима:

РежимМетодОтвет**field**`ElementAjaxHandler::handleField()``ID`, `CLASS`, `ERROR` (через `AjaxRenderStrategyInterface`)**form/block**`ElementAjaxHandler::handleForm()` / `handleBlock()``ID`, `HTML` (через стратегию, напр. `BootstrapAjaxRenderStrategy`)Поиск элемента по DOM-id: `ElementDataCollector::findById()` или `$form->getById()`.
Короткое имя поля — `getByName()`; для AJAX используйте `getId()` / `getFormName()`.

### Разметка AJAX на форме и полях

[](#разметка-ajax-на-форме-и-полях)

На `Form`, `InputText`, `InputSubmit` и других элементах с `HtmlAttributes` доступны:

```
$form->setMethod('get')
    ->setAjax(true)
    ->setUrlState('replaceState'); // 'pushState' | false — не менять URL

$input = (new InputText('name'))->setAjax();
```

В HTML: `data-fg-ajax="true"`, опционально `data-fg-url-state="replaceState"`.
`setAjax()` на **форме** — перехват `submit` и (в demo) `change` на полях фильтра; на **поле** — field mode (`action` = `getId()`).

В demo URL state и AJAX POST используют **одни и те же ключи**, что `collectPageData()` (`getFormName()` из DOM): например `demoSettings_decoratorFieldset_decorator`, `fl_gc_cat[]`, `page`. Decorator читается только по полному ключу `demoSettings_decoratorFieldset_decorator` (или из session).

### Endpoint (пример)

[](#endpoint-пример)

```
use Kavalhub\FormGenerator\Ajax\AjaxRequest;
use Kavalhub\FormGenerator\Ajax\ElementAjaxHandler;
use Kavalhub\FormGenerator\Bootstrap\BootstrapAjaxRenderStrategy;
use Kavalhub\FormGenerator\Request\ElementRequest;
use Kavalhub\FormGenerator\Validator\ElementValidator;

header('Content-Type: application/json; charset=utf-8');

if (!AjaxRequest::isXmlHttpRequest()) {
    http_response_code(400);
    exit;
}

$validator = new ElementValidator(new ElementRequest());
$handler = new ElementAjaxHandler($validator, new BootstrapAjaxRenderStrategy());
$form = /* ваша форма */;

if ($targetId = AjaxRequest::readTargetId()) {
    echo $handler->handleField($form, $targetId)->jsonEncode();
    exit;
}

if ($validator->checkSubmit($submit) && $validator->handle($form)) {
    echo $handler->handleBlock($table)->setMessage('Сохранено')->jsonEncode();
}
```

Параметр `action` (или `target_id`) = `getId()` поля, как в demo.

### Клиент (минимальный пример, не входит в пакет)

[](#клиент-минимальный-пример-не-входит-в-пакет)

```
function collectPageData() {
    const body = new FormData();
    document.querySelectorAll('form').forEach((form) => {
        new FormData(form).forEach((value, key) => body.append(key, value));
    });
    return body;
}

function applyUrlState(form) {
    const mode = form?.dataset?.fgUrlState;
    if (!mode) return;
    const params = new URLSearchParams();
    collectPageData().forEach((value, key) => params.append(key, value));
    const url = `${location.pathname}?${params}`;
    (mode === 'pushState' ? history.pushState : history.replaceState).call(history, null, '', url);
}

document.querySelector('[data-fg-ajax="true"]').addEventListener('input', function () {
    const fd = collectPageData();
    fd.set('action', this.id);
    fd.set(this.name, this.value);
    fetch('/ajax.php', { method: 'POST', body: fd, headers: { 'X-Requested-With': 'XMLHttpRequest' } })
        .then(r => r.json())
        .then(data => {
            data.REPLACE.forEach(patch => {
                const el = document.getElementById(patch.ID);
                el.classList.remove('is-valid', 'is-invalid');
                if (patch.CLASS) el.classList.add(patch.CLASS);
                el.parentElement.querySelectorAll('.invalid-feedback').forEach(n => n.remove());
                if (patch.ERROR) el.insertAdjacentHTML('afterend', patch.ERROR);
                if (patch.HTML) document.getElementById(patch.ID).outerHTML = patch.HTML;
            });
            applyUrlState(this.closest('form[data-fg-url-state]'));
        });
});
```

Живой пример с переключателем «классика / AJAX», синхронизацией URL (`setUrlState`) и восстановлением фильтра из GET — demo-проект `kavalhub/form-demo`: главная демонстрация на `?page=filter` (фильтр товаров, чекбоксы/радио на лету), также `?page=facet` (добавление фасета).

JSON API (3.2+)
---------------

[](#json-api-32)

Структурированный обмен без HTML-патчей: валидация и сабмит формы через JSON.

КлассНазначение`Request\JsonElementRequest`Источник данных из JSON / массива`Api\FormApiHandler``handleField()` / `handleForm()` → `FormApiResponse``Api\FormJsonSchemaExporter`JSON Schema полей формы (интроспекция дерева)`Api\OpenApiDocumentBuilder`Сборка OpenAPI 3.0 из списка форм```
use Kavalhub\FormGenerator\Api\FormApiHandler;
use Kavalhub\FormGenerator\Request\JsonElementRequest;
use Kavalhub\FormGenerator\Validator\ElementValidator;

$request = JsonElementRequest::fromArray(['contact_email' => 'user@example.com']);
$handler = new FormApiHandler(new ElementValidator($request));
$response = $handler->handleForm($form);
echo $response->jsonEncode(); // {"valid":true,"fields":{...},"data":{...}}
```

OpenAPI и наполнение БД через JSON — demo: `GET /api.php`, `POST /api.php`, Swagger UI на `/api-docs.html`.

Сбор данных из дерева элементов
-------------------------------

[](#сбор-данных-из-дерева-элементов)

```
use Kavalhub\FormGenerator\Util\ElementDataCollector;

$data = ElementDataCollector::collectByFormName($form);
// ['contact_email' => 'user@example.com', ...]
```

Поддерживаемые элементы
-----------------------

[](#поддерживаемые-элементы)

КлассОписание`Html\Form`Контейнер ```Html\Group`Группа полей с префиксом имени`Html\InputText`, `Html\InputPassword`, `Html\InputNumber`Текстовые поля`Html\InputCheckbox`, `Html\InputRadio`Переключатели`Html\Select`, `Html\Option`Выпадающий список`Html\Textarea`Многострочный ввод`Html\InputHidden`, `Html\InputSubmit`, `Html\Button`Скрытые и кнопки`Html\Label`, `Html\Nav`, `Html\Link`Разметка`Html\Table\Table`, `Html\Table\Tr`, `Html\Table\Td`, `Html\Table\Th`ТаблицыМиграция 2.x → 3.x
------------------

[](#миграция-2x--3x)

- Namespace виджетов: `Kavalhub\FormGenerator\Form\*` → `Kavalhub\FormGenerator\Html\*`
- Таблицы: `Kavalhub\FormGenerator\Table\*` → `Kavalhub\FormGenerator\Html\Table\*`
- Рендеринг элементов: `getHtml()` → `render()` (декораторы по-прежнему используют `getHtml()`)
- `Element` — доменная модель без HTML; HTML-трейты и виджеты в `src/Html/`
- `HtmlEscaper` перенесён в `Kavalhub\FormGenerator\Html\Util\HtmlEscaper`
- Базовые HTML-классы: `HtmlElement`, `HtmlElementWithValue`, `HtmlCompositeElement` — содержат `tag`, `ClassList`, `Path`
- Доменный `Element` не имеет `tag`, `getTag()`, `addClass()` — только дерево, значения и валидация

Безопасность
------------

[](#безопасность)

- Значения полей, placeholder, href и сообщения об ошибках экранируются через `Html\Util\HtmlEscaper`.
- `Label::setAllowHtml()` — явное разрешение HTML в подписи.
- `ElementRequest` использует `$_REQUEST` — удобно для GET-фильтров; для POST-only используйте `PostOnlyRequest`.
- CSRF включается явно через `Form::enableCsrf()`.

Тесты
-----

[](#тесты)

```
composer install
composer test
```

Лицензия
--------

[](#лицензия)

MIT

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance91

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~25 days

Recently: every ~1 days

Total

19

Last Release

44d ago

Major Versions

1.0.13 → 2.0.02026-07-01

2.0.1 → 3.0.02026-07-05

PHP version history (2 changes)1.0.0PHP ~8.2.0

2.0.0PHP ^8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/204548407?v=4)[kavalhub](/maintainers/kavalhub)[@kavalhub](https://github.com/kavalhub)

---

Top Contributors

[![kavalhub](https://avatars.githubusercontent.com/u/204548407?v=4)](https://github.com/kavalhub "kavalhub (23 commits)")

---

Tags

phpformValidation form

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kavalhub-form-generator/health.svg)

```
[![Health](https://phpackages.com/badges/kavalhub-form-generator/health.svg)](https://phpackages.com/packages/kavalhub-form-generator)
```

###  Alternatives

[laminas/laminas-form

Validate and display simple and complex forms, casting forms to business objects and vice versa

8213.1M143](/packages/laminas-laminas-form)[progsmile/request-validator

Simple PHP Request Validator

33115.0k1](/packages/progsmile-request-validator)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
