Purpose

The editor-side hook for persisting fields a plugin added to the page form. When PagesModule is about to save a page (create or update), it dispatches this event with the about-to-be-saved item. A listener pulls its inputs off the request and calls mergeData() to fold extra keys into the item's data bag before the write.

It is the persistence half of PageFormRendering: that event renders the input, this one saves the posted value. Added in Scriptor 2.1.0.

FQCN + file path

When to use

Subscribe inside Plugin::register(), alongside a PageFormRendering listener, to store whatever that listener rendered. Read the value with $event->input->postString('…') and hand it to mergeData().

This event has no error or veto channel: a listener can add data but cannot block the save or attach a validation error. Sanitise and normalise the value in the listener (trim, collapse whitespace, route through the Sanitizer); if a value is invalid, store a cleaned version rather than trying to reject the save here.

Surface

Public properties

Property Type Purpose
item readonly Item The iManager item about to be saved, core fields already populated from the form
input readonly Request Per-request input bag; listeners pull their own POST fields from it

Public methods

public function mergeData(array $extra): void

Merge keys into the data the page will be saved with. Called any number of times; later keys override earlier ones on collision. PagesModule applies the accumulated set after dispatch.

public function extraData(): array

Returns everything merged so far. Called by PagesModule to read the result; listeners rarely need it.

Constructor

public function __construct(public readonly Item $item, public readonly Request $input)

PagesModule constructs it. You do not construct it yourself.

Lifecycle

Fires inside PagesModule's save action, after the core Item is assembled from the posted core fields and before the item is written to storage. After dispatch, PagesModule checks extraData(): if non-empty, it rebuilds the item with data: [...$coreData, ...$saving->extraData()] and saves that. When no listener merged anything, the save runs unchanged, so the event is zero-cost when no plugin subscribes.

Because the core data is spread first and extraData() second, a merged key whose name collides with a core key wins. In normal use the keys are plugin-private (meta_title, price) and do not collide.

Common patterns

Persist the fields a PageFormRendering listener rendered

$context->subscribe(
    \Scriptor\Boot\Events\Editor\PageSaving::class,
    function (\Scriptor\Boot\Events\Editor\PageSaving $event): void {
        $event->mergeData([
            'meta_title' => trim($event->input->postString('meta_title')),
        ]);
    },
);

Gate persistence on a page-type

$context->subscribe(
    PageSaving::class,
    function (PageSaving $event): void {
        if (($event->item->data->get('template') ?? '') !== 'produkt') {
            return;
        }
        $event->mergeData([
            'price'   => trim($event->input->postString('price')),
            'deposit' => trim($event->input->postString('deposit')),
        ]);
    },
);

Read the template from $event->item->data rather than a Page object: at save time the event carries the raw iManager item, and its data bag already holds the posted template value.

See also