Problem/Motivation
The default content system added with the drush content:export command (Drupal\Core\DefaultContent\Exporter / Importer, #3532694: Add a command-line utility to export content in YAML format) cannot round-trip a field of type map:
- Export drops the data.
Exporter::exportFieldItem()iterates$item->getProperties(). Amapfield item (MapItem) defines no static property definitions —propertyDefinitions()returns[]— sogetProperties()is empty and the field exports as- { }(an empty map), losing all stored data. - Import throws.
Importer::setFieldValues()treats each key of a delta as a typed-data property and calls$item->get($property_name). For amapitem every property name is unknown, so import aborts withInvalidArgumentException: Property <key> is unknown.
There is no way for a site or module to make a populated map field survive content:export + import. The export side has an extension point (PreExportEvent::setCallback()), but the import side has no field-type callback at all, so even a custom export format cannot be read back in.
This affects any content entity with a populated map field / base field — for example ai_context's ai_context_item.scope, and other modules that use MapItem for arbitrary serialized data.
Steps to reproduce
- On a standard install, take any content entity type with a
mapfield (or add one). Store keyed data in it, e.g.['use_case' => ['working_with_media']]. - Export it:
drush content:export <entity_type> <id> --dir=…(or callExporter::export()). Inspect the YAML — themapfield is- { }; the data is gone. - Hand-author the real map value into the YAML
defaultsection and import it (Importer::importContent()). Import aborts withProperty <key> is unknown.
Reproduced on core 11.3.14.
Proposed resolution
Special-case map fields in both directions, since a map item's value is the whole array rather than a set of named properties:
Exporter::exportFieldItem()— when the field type ismap, return$item->getValue()(the whole stored array) instead of walking properties.Importer::setFieldValues()— when the field type ismap, call$item->setValue($item_value)for the whole delta and skip the per-property loop.
The two sides are symmetric: the exporter writes the delta as the map array, and the importer reads that same shape straight back with setValue().
// Exporter::exportFieldItem(), before the getProperties() loop:
if ($item->getFieldDefinition()->getType() === 'map') {
return $item->getValue();
}
// Importer::setFieldValues(), right after $item = …->get($delta):
if ($item->getFieldDefinition()->getType() === 'map') {
$item->setValue($item_value);
continue;
}
Remaining tasks
- Review the approach: a type check on
'map'vs. a broader "field item exposes no properties" and check for all-computed-property fields. - Add test coverage: a kernel test that exports and re-imports an entity with a populated
mapfield and asserts the value survives (export is non-empty, import does not throw, stored value matches). - Confirm behaviour for empty
mapvalues (should export- { }and import to an empty value without error.
User interface changes
None.
Introduced terminology
API changes
None. Behavioural fix only: map fields that previously exported empty and could not be imported now round-trip their stored value. No signatures change.
Data model changes
Release notes snippet
The default content system (drush content:export and the DefaultContent importer) now correctly exports and imports map field values, which were previously dropped on export and rejected on import.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3617512
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
jibranUploading a patch for now. This isn't a proper fix, but it gets the job done.
Comment #3
jibranAdded related issue from ai_context module Replace the context item scope map field with a multi-value scope_id + value field.
Comment #5
jibranCreated https://git.drupalcode.org/project/drupal/-/merge_requests/16743