Problem/Motivation
Currently, JavaScriptComponent config entities can declare data dependencies via dataDependencies.urls (external URLs fetched by the component) and dataDependencies.drupalSettings (Canvas Drupal settings needed by the component). However, there is no way for a JS component to declare that one of its props depends on entity field data.
This is the first foundational step toward #3573831: [META] Code Components: add an "entity reference" prop type — enables "view modes" that combine multiple entities plus static inputs!. Before the UI, runtime evaluation, or entity reference prop type can be built, the config schema needs to support declaring which entity fields each prop depends on.
Proposed resolution
Add a new optional entityFields key to the dataDependencies mapping on JavaScriptComponent config entities (canvas.js_component.*).
Schema structure:
entityFieldsis optional (requiredKey: false), asequenceordered by key- Each key is a prop name from the component's
props(validated viaSequenceKeysMustMatchinsubsetmode) - Each value is an array of field prop expression strings, validated by
ValidStructuredDataPropExpressionwith allowed types:FieldPropExpressionFieldObjectPropsExpressionReferenceFieldPropExpression
Validation constraints:
SequenceKeysMustMatch(extended): gained amatchTypeparameter with valuessame-set(default, pre-existing behavior — validated sequence must contain exactly the same keys as the target sequence) andsubset(new — validated sequence's keys must all exist in the target sequence, but missing keys are allowed). Used here insubsetmode soentityFieldskeys must be a subset of the component'sprops— only entity-reference props will haveentityFieldsentries. Preferred over introducing a separateSequenceKeysAreSubsetOfconstraint to avoid duplicating the sequence-comparison logic.EntityFieldExpressionsSameTarget(new): ensures all expressions within a single prop target the same entity type and bundle.ExpressionTargetEntityBundleExists(new): validates entity type existence and bundle existence for each expression string. (Originally scoped asValidEntityFieldExpressionTarget; renamed for clarity — the constraint is reusable beyond theentityFieldscontext.)- Entity reference props cannot be required: props with
entityFieldsentries must not appear inrequired(validated inJsComponentHasValidAndSupportedSdcMetadata). A referenced entity may disappear, so the component must tolerateNULL.
Config dependency calculation:
Override JavaScriptComponent::calculateDependencies() so each expression in dataDependencies.entityFields contributes its module and config dependencies (field configs, bundle configs, entity type provider modules) to the saved config entity. Follows the same addDependencies() pattern already used by ContentTemplate, Pattern, and PageRegion for component-tree expressions. Without this, ConfigDependencyManager would not know about the field/bundle/module references, breaking cascading deletes, import ordering, and cache invalidation.
OpenAPI:
The CodeComponent schema in openapi.yml is updated to document entityFields as an object with additionalProperties of type array<string>.
Remaining tasks
Review and commit.
User interface changes
None.
API changes
The dataDependencies object in API responses for code components (GET /canvas/api/v0/config/js_component/*) may now include an entityFields key. This is a backwards-compatible addition — existing responses without entityFields remain valid.
Data model changes
New optional key in canvas.js_component.* config schema:
dataDependencies: mapping: entityFields: requiredKey: false type: sequence orderby: key constraints: NotBlank: message: "There must be >=1 entity reference prop; otherwise the 'entityFields' key should be omitted." SequenceKeysMustMatch: matchType: subset propertyPathToSequence: props sequence: type: sequence orderby: value constraints: NotBlank: message: 'There must be >=1 entity field expression; otherwise the entity reference prop should be deleted.' EntityFieldExpressionsSameTarget: ~ sequence: type: string constraints: ValidStructuredDataPropExpression: choices: - FieldPropExpression - FieldObjectPropsExpression - ReferenceFieldPropExpression ExpressionTargetEntityBundleExists: ~
No database changes. No config migration needed (requiredKey: false means existing JS components pass validation without changes).
The dependencies array on canvas.js_component.* exports may now include new module/config entries derived from entityFields.
Deferred to follow-ups
- Filter
SequenceKeysMustMatch(insubsetmode) to only entity reference props, rather than all props — requires the entity reference prop type from #3573831: [META] Code Components: add an "entity reference" prop type — enables "view modes" that combine multiple entities plus static inputs!. x-allowed-bundlevalidation for bundled entity types (e.g., requireentity:node:articleinstead ofentity:node) — also requires the entity reference prop type. Test coverage exists as a skipped test.
Issue fork canvas-3585298
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
Comment #2
penyaskitoIsn't this data model component?
Comment #4
penyaskitoUpdated issue-summary.
Comment #5
penyaskitoComment #6
penyaskitoRemoving phpstan rule from the IS: that landed already in #3585319: PHPStan: Test methods should never have optional parameters from a data provider
Comment #7
wim leersIndeed! 😄🚀
Nice work on
SequenceKeysAreSubsetOf,EntityFieldExpressionsSameTargetandValidEntityFieldExpressionTarget! I thinkValidEntityFieldExpressionTargetshould be renamed though, and I thinkSequenceKeysAreSubsetOfcould be done in a way that reduces maintenance cost.I would've expected this to introduce the updated
JavaScriptComponent:: calculateDependencies()too?Approved with remarks 😊 None of my feedback is merge-blocking; I believe all of it would make future work simpler, but I defer to your judgment, @penyaskito.
Comment #8
penyaskitoAdded config dependency calculation to the IS.
Comment #9
penyaskitoReviewed all feedback + extra test coverage for
calculateDependencies()Comment #10
penyaskitoUpdated IS
Comment #11
longwaveTiny bikeshed comment about naming things but otherwise this all looks good to go to me.
Comment #13
longwave