Search by

uxf / page-builder

uxf

UXF Page Builder - CMS page/section versioning system

Package info

gitlab.com/uxf/page-builder

Issues

Type:symfony-bundle

pkg:composer/uxf/page-builder

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 0

3.88.0 2026-11-06 09:58 UTC

This package is auto-updated.

Last update: 2026-11-06 09:02:39 UTC


README

Symfony bundle for pages built from sections: pages and their revisions, sections and shared sections, publishing and scheduled publishing, previews, the REST API for the backoffice editors and the public read API.

The bundle holds no section types. The project defines them (request class with #[SectionField] attributes, a SectionContentMapper and a response) and registers its layouts, groups and languages in a registry JSON.

Frontend counterparts: @uxf/page-builder (contract), @uxf/page-builder-cms (backoffice editors) and @uxf/page-builder-web (renderer, editor iframe, previews).

Install in a project

composer require uxf/page-builder

The bundle needs uxf/core, uxf/datagrid, symfony/security-bundle and a Symfony security setup that resolves Symfony\Component\Security\Core\User\UserInterface to a concrete user entity (e.g. via uxf/security or uxf/cms).

Setup

Register the bundle in config/bundles.php:

UXF\PageBuilder\UXFPageBuilderBundle::class => ['all' => true],

Import its routes in config/routes.php:

$routingConfigurator->import('@UXFPageBuilderBundle/config/routes.php');

The routes are /api/cms/page-builder/* (editors) and /api/app/page-builder/* (public read API). The editors' routes (the bundle's Controller\Admin controllers, under whatever path the project mounts them) need the editor_role (default ROLE_ADMIN) on top of the project's access_control; keep the project's ^/api/cms rule too.

Configuration

$containerConfigurator->extension('uxf_page_builder', [
    // role allowed to publish, unpublish and archive; other admins edit drafts only
    'publisher_role' => 'ROLE_ROOT',
    // role every editors' route needs; null turns the bundle's check off
    'editor_role' => 'ROLE_ADMIN',
    // the project's registry JSON (layouts, groups, section types, languages), the one source of it;
    // null = nothing is restricted
    'registry' => '%kernel.project_dir%/config/page-builder/registry.json',
    // `uxf:page-builder:schema-gen` writes section-schemas.json and registry.json into each directory
    'gen' => [
        'destinations' => [
            '%kernel.project_dir%/../web/src/generated/page-builder-schema',
            '%kernel.project_dir%/../backoffice/src/generated/page-builder-schema',
        ],
    ],
    // where `uxf:page-builder:make-section` creates a new section type (these are the defaults)
    'make_section' => [
        'directory' => '%kernel.project_dir%/src/PageBuilderZone/SectionContent',
        'namespace' => 'App\PageBuilderZone\SectionContent',
    ],
]);

Add the bundle sources to the hydrator map sources, so the section requests and the bundle DTOs share one map:

$containerConfigurator->extension('uxf_hydrator', [
    'map_sources' => [
        __DIR__ . '/../../src',
        __DIR__ . '/../../vendor/uxf/page-builder/src',
    ],
]);

Database

The bundle ships no migrations. Generate them in the project after installing or upgrading the bundle:

bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate

Generated clients

The backoffice client comes from the project's uxf_gen hook area. Include the page builder paths in its pattern:

'hook' => [
    'areas' => [
        'admin' => [
            'path_pattern' => '/^\/api\/(app\/page-builder|admin|cms)/',
            'output' => 'swr',
            'destination' => '%kernel.project_dir%/../backoffice/src/generated/api.ts',
        ],
    ],
],

The web's types of the section outputs come from a second area with only the render endpoint (output: empty): its union of the public outputs (PageBuilderSectionContentPublicOutput) types the web's section components.

bin/console uxf:page-builder:schema-gen (in the project's make gen, next to uxf:grid-gen and uxf:form-gen) writes the section schemas (section-schemas.json, from the section requests) and the registry (registry.json) into each gen.destinations directory. Like the grid and form schemas, the apps generate their TypeScript from them: uxf-page-builder-generator of @uxf/page-builder writes registry.ts (registry, ProjectSectionType) and section-schemas.ts (sectionSchemas), run from the apps' yarn gen. The registry JSON stays in api/ (the API reads it, and its image is built from api/ alone); nothing has to copy it into the apps.

The registry is read and checked during cache:warmup: a missing or broken file fails the deploy, not the first request.

Section types

bin/console uxf:page-builder:make-section quote --label=Citace creates the request, the response, the public response and the mapper of a type with one translatable title, all keyed by the type, and prints its registry entry with an example in every language. Then add the type to the registry and to the groups that take it, add its fields, run make gen and write the web component.

The type string is the key everywhere: #[HydratorMapKey] on the three DTOs, their $type default, #[AsTaggedItem(index:)] on the mapper, the registry key and its example.type. Stored content is never migrated: mapToOutput gives a field added later a default.

Public read API

RouteAnswer
GET /api/app/page-builder/public/page-by-url?url=/o-nas&lang=csthe page published at the URL (a relative URL, UrlRelative), for the web's catch-all route
GET /api/app/page-builder/public/page/{page}?lang=csthe same by the page's id
GET /api/app/page-builder/public/preview/{token}?lang=csa revision shared through a time-limited link

The page answer carries the page's id, layout (the registry layout the web arranges the sections by), url, the meta data in the language, noIndex and the sections with their public output. A URL without a published page, a language outside the registry and a page hidden in the language answer 404.

Shared contract

test-cases/ holds the files the npm packages share: section-validation.json (the section validation cases both sides run) and api-routes.json (the routes the packages call, checked against the bundle's router by ApiRoutesContractTest). Change them together with @uxf/page-builder in fe-monorepo.

Scheduled publishing

bin/console uxf:page-builder:publish-scheduled publishes approved page and shared section revisions whose scheduled time has passed. It carries #[CronJob('* * * * *')] (uxf/cms), an optional integration: when uxf/cms is installed, it lands in the project's crontab once the bundle sources are among the cron sources:

$containerConfigurator->extension('uxf_cms', [
    'cron' => [
        'sources' => [__DIR__ . '/../../src', __DIR__ . '/../../vendor/uxf/page-builder/src'],
        'destinations' => [__DIR__ . '/../../crontab'],
    ],
]);

Then run bin/console uxf:cron-gen and run the crontab with supercronic. The command takes a lock (symfony/lock), so overlapping runs skip.

Without uxf/cms, add the command to the project's crontab by hand instead:

* * * * * bin/console uxf:page-builder:publish-scheduled

License

MIT