SeviaFlow Get Started

Forms & Sections

Forms group fields for create/edit and show surfaces.

Fields

use SeviaFlow\Core\Resources\Fields\EmailField;
use SeviaFlow\Core\Resources\Fields\TextField;
use SeviaFlow\Core\Resources\Form;

public static function form(Form $form): Form
{
    return $form->fields([
        TextField::make('name')->label('Name')->required(),
        EmailField::make('email')->label('Email')->required(),
    ]);
}

Form::getFields() returns the direct field list.

Sections

use SeviaFlow\Core\Resources\Section;

public static function form(Form $form): Form
{
    return $form->sections([
        Section::make('Identity')
            ->description('Primary customer information.')
            ->fields([
                TextField::make('name')->required(),
                EmailField::make('email')->required(),
            ]),
    ]);
}

Section::make(string $label) creates a titled group of fields. Configure the section's description and layout before assigning its fields:

MethodWhat it controls
description(...)Adds supporting text below the section heading.
fields([...])Places the supplied field definitions inside this section.
columnSpan(...)Sets the section's width in the form grid. Use 'full' to span all 12 columns, or an integer from 1 to 12.
collapsible()Lets the operator expand or collapse the section.
collapsed()Sets the section's initial collapsed state. Use it together with collapsible() when the operator should be able to reopen it.

A section organizes fields visually; it does not create a nested database object. Each field still refers to its own model attribute.

Form layout methods

MethodWhat it controls
fields([...])Adds fields directly to the form without a section or tab.
sections([...])Groups fields into titled sections.
tabs([...])Groups fields into separate tab panels.
columns(2)Sets the form grid width from 1 to 12 columns.

All Fields

Form::getAllFields() collects direct fields, section fields, and tab fields into one list. Core uses it when it needs every field in the form, including for API validation reuse.

Show Forms

Resource::show(Form $form): Form defaults to static::form($form). Override it when the show page needs a different set of fields or context visibility rules.

Absence, null and empty values

SeviaFlow distinguishes an omitted key from an explicit value in forms, API payloads and CSV imports. An omitted create key preserves the default defined by the field, model or database; an omitted update key preserves the stored value. Explicit null clears a nullable field and is rejected for required or non-nullable fields.

InputContract
Omitted keyPreserve the create default or existing update value.
nullClear nullable fields; validate required fields.
''Normalize according to the field type; text fields normalize blank text to null.
false, 0, "0"Persist as supplied; these values are never treated as absence.
Empty list/objectClear a supported collection when the key is explicitly present.

Create forms render empty placeholders, but only fields with a field default or a user change are submitted. Password fields configured to dehydrate only when filled retain their previous value; file fields continue to use retain, remove and replace.

Concurrent edits

Core, UI and API use the same record revision token. API reads expose it as a quoted ETag; send that value back as If-Match when updating a record:

curl -X PATCH https://example.test/api/admin/resources/customers/42 \
  -H 'Content-Type: application/json' \
  -H 'If-Match: "<etag-from-the-read>"' \
  -d '{"name":"Updated customer"}'

A stale token returns 409 with errors.revision; no successful event, workflow transition or file lifecycle side effect runs. A malformed token returns 422. Updates without If-Match remain available for legacy clients and are marked X-SeviaFlow-Concurrency: unprotected; migrate those clients by reading ETag, sending If-Match and handling the conflict explicitly.

The UI keeps typed values after a conflict and offers an explicit refresh action that discards local changes. Core rechecks the resource scope and write authorization on the locked record, so the token never replaces access control.