Problem/Motivation

DataDefinitionInterface can describe a value's type, label, description, settings, constraints and whether it is required — but not what value it starts from. Because the typed data layer can't express a default, every layer built on top of it has grown its own way of saying the same thing:

  • Field API: FieldDefinitionInterface::getDefaultValue(), getDefaultValueLiteral() and getDefaultValueCallback().
  • Plugin contexts: ContextDefinition::getDefaultValue() / setDefaultValue(), bolted onto a class that is otherwise a DataDefinition wrapper.
  • Config: defaults live outside the schema entirely, in each module's config/install YAML.
  • Contrib APIs that describe inputs with data definitions (ECA, Typed Data API enhancements, tool-style input definitions) each re-add a default value property to their own wrapper.

Each of these is a parallel spelling of one concept the base definition can't carry. That has practical costs beyond duplication:

  • Core can now emit JSON Schema from normalizers (the json_schema serialization format, \Drupal\Core\Serialization\Attribute\JsonSchema). JSON Schema has a standard default keyword, but a schema derived from typed data can never populate it — the source metadata has nowhere to hold one. The same gap applies to anything producing OpenAPI output from typed data.
  • Code that generates UI or documentation from a data definition (form builders, schema browsers, API docs) has to special-case every wrapper that happens to have its own default API, and gets nothing for plain definitions.
  • DataDefinition::createFromDataType('email') and friends give you a fully described value space with no way to suggest a starting point, even when the defining code knows one.

This was almost in core once: #2204509 originally proposed defaults for typed data definitions and context definitions together, and was deliberately narrowed to context definitions only — the typed data half was considered a nice addition that wasn't needed for the driving use case at the time (comment #12). The landscape has changed since: core now derives JSON Schema from typed data, which gives the deferred half a concrete consumer.

Steps to reproduce

Not applicable (feature request). For a quick illustration of the gap: build any DataDefinition and look for a way to declare what value it should start from — there isn't one, which is why ContextDefinition and FieldDefinitionInterface both carry their own.

Proposed resolution

Add default value support to DataDefinition as plain metadata, stored under a default value key in the definition array like the existing properties:

  • setDefault($value): static
  • getDefault(): mixed

No behavior changes anywhere: TypedDataManager::create() keeps instantiating with whatever value it is given, and validation is untouched. The default is descriptive metadata that consumers opt into — schema emitters map it to the JSON Schema default keyword, form generators use it to prepopulate, and the layered APIs above could (in follow-ups, if desired) delegate to it instead of maintaining their own copies.

Naming needs care, per the discussion in #2204509: BaseFieldDefinition extends DataDefinition and implements FieldDefinitionInterface, whose getDefaultValue(FieldableEntityInterface $entity) takes a required argument — a no-arg getDefaultValue() on the base class would collide. Options include a distinct name for the definition-level accessor, or aligning with the no-arg getDefaultValueLiteral() shape the field layer already has. Whether the methods land on DataDefinitionInterface itself or only on the DataDefinition base class first is a second BC question for this issue to settle.

Remaining tasks

User interface changes

None.

Introduced terminology

None.

API changes

Three new methods on DataDefinition (and potentially DataDefinitionInterface, subject to the BC discussion above). Purely additive; no existing method changes signature or behavior.

Data model changes

None. The definition array gains an optional key.

Release notes snippet

Data definitions can now declare a default value via DataDefinition::setDefault(). The default is descriptive metadata available to consumers such as JSON Schema emitters and form generators; it does not change how typed data is instantiated or validated.

AI usage (if applicable)

  • [x] AI Assisted Issue: This issue was generated with AI assistance, but was reviewed and refined by the creator.
  • [ x] AI Assisted Code: This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.

Issue fork drupal-3620920

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

michaellander created an issue. See original summary.

michaellander’s picture

Assigned: Unassigned » michaellander

michaellander’s picture

Issue summary: View changes
michaellander’s picture

Assigned: michaellander » Unassigned
Status: Active » Needs review
michaellander’s picture

Status: Needs review » Needs work

I called out the emitting of default values to json schema as a separate issue, but would it be more appropriate to include as part of this? Setting back to needs work to remove the `hasDefault()` method, as it's inconsistent with the rest of the class.

michaellander’s picture

Status: Needs work » Needs review

I created a separate issue for emitting to json schema #3623431: Emit data definition default values as the JSON Schema "default" keyword. Please let me know if the preference is to combine them.

michaellander’s picture

Issue summary: View changes