Skip to content

feat!: define meta fields as typed properties using the form system - #6760

Merged
gharlan merged 6 commits into
6.xfrom
meta-form-system
Oct 20, 2026
Merged

gharlan merged 6 commits into
6.xfrom
meta-form-system

Conversation

@gharlan

@gharlan gharlan commented Oct 20, 2026

Copy link
Copy Markdown
Member

Moves meta info onto the new form system (#6754): a meta schema declares its fields as typed properties annotated with form field types, the same way as any other form data class. The old MetaInfo\Field\* classes are removed.

Schemas

#[AsMetaSchema(MetaEntity::Article)]
final class SeoMeta extends MetaSchema
{
    #[Text(
        'SEO title',
        maxLength: 100,
    )]
    #[Translated]
    public string $seoTitle = '';          // rex_article_translation.meta_seo_title

    /** @var list<string> */
    #[Choice(
        'Tags',
        choices: ['news' => 'News', 'blog' => 'Blog'],
        multiple: true,
        expanded: true,
    )]
    public array $tags = [];               // rex_article.meta_tags, comma-separated

    public static function isFieldAllowed(string $property, MetaContext $context): bool
    {
        return 'tags' !== $property || null !== $context->category;
    }

    public function validate(): string|Translatable|null
    {
        return null;
    }
}

SeoMeta::of($article)->seoTitle;
  • Columns: meta_ + the field key, i.e. the property name in snake_case ($seoTitle → meta_seo_title) or an explicit key – which also allows renaming a property without losing its column. The column types are derived from the field and property types (e.g. Text → varchar(maxLength ?? 255), Textarea and lists → text, Checkbox → bool, date pickers → date/datetime/time, float → double, BcMath\Number → decimal with the scale of the field's step); a schema can override single columns via column(). Lists are stored comma-separated, so they stay queryable via FIND_IN_SET(). Column::double() is added for this.
  • #[Translated] marks fields stored per language (in the translation table of the entity).
  • isFieldAllowed() restricts fields to a context (e.g. a category), validate() checks across fields.
  • Several schemas per entity are allowed now (e.g. one of an addon and one of the project); colliding columns throw a LogicException.
  • Typed access: MetaSchema::of($item) returns the meta values of an article, category, medium or language as a schema instance, shared per item. Missing values or values that do not fit the property type anymore keep the property default.

Forms and validation

The handlers embed one form per schema into the forms of the entities (Form::submit()/only from #6754). Submitted values are validated now – before, they were saved without any checks – and invalid values are not saved:

  • article meta form: the errors are shown at the fields, the submitted values stay;
  • categories and media: the meta values are saved after the category/medium itself (via CategoryAdded/CategoryUpdated/MediaAdded/MediaUpdated), so the entity is saved already; the errors are reported via UserMessageException ("The metadata has not been saved: …"), which the API functions and the media pool show as error message.

Validating the meta values before the entity is saved would need meta info to be integrated into the handlers directly instead of via extension points – possible now that meta info is part of the core, but left for a follow-up.

Also fixed

  • The category edit form read the meta values from a row of the article tables, which lacks the meta columns of the category tables (since categories have their own tables). It now reads them from the Category object.
  • The English label of the media usage check for media listed "Languages" instead of "Media".

A meta schema now declares its fields as properties annotated with form
field types instead of yielding `MetaField` objects. The columns are named
after the field keys (the property name in snake_case or an explicit
`key`) with the `meta_` prefix, their types are derived from the field and
property types (`float` → `double`, `BcMath\Number` → `decimal` with the
scale of the field's step). `#[Translated]` marks fields stored per
language, `isFieldAllowed()` restricts fields to a context, `validate()`
checks across fields.

`MetaSchema::of($item)` returns the typed values of an article, category,
medium or language. An entity may now have several schemas.

The handlers embed one form per schema into the forms of the entities.
Submitted values are validated now; invalid ones are not saved. The
article meta form shows the errors at the fields, categories and media
report them via `UserMessageException`.

`Column::double()` is added for the approximate number columns.
@gharlan gharlan added this to the REDAXO 6.0 milestone Oct 20, 2026
@rex-bot rex-bot added breaking feature Additional functionality labels Oct 20, 2026
gharlan and others added 5 commits October 20, 2026 21:16
The meta sidebar of the article is too narrow for labels next to the
fields. `FieldRow` and `Form` get a `vertical` option for that, using the
existing `rex-form-group-vertical` layout.
@gharlan
gharlan merged commit e785e56 into 6.x Oct 20, 2026
15 checks passed
@gharlan
gharlan deleted the meta-form-system branch October 20, 2026 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking feature Additional functionality

Development

Successfully merging this pull request may close these issues.

2 participants