Problem/Motivation
MarkupItem::isEmpty() inspects the configured markup text rather than the item's own value:
public function isEmpty() {
$value = $this->getFieldDefinition()->getSetting('markup')['value'];
return $value === NULL || $value === '';
}Any usefully-configured markup field has non-empty settings, so this returns FALSE for every item, including items with no data.
Markup fields never store data: MarkupWidget::formElement() renders processed_text from field settings and collects no input. But building a widget calls FieldItemList::get($delta), which auto-creates a missing item. So merely rendering an entity form leaves an empty ['markup' => NULL] item on the entity, and because isEmpty() reports it as non-empty, filterEmptyItems() never removes it.
Two consequences:
- Rows of NULLs accumulate in the field tables — on our site, 1,080 rows with 0 non-null values. (This is the underlying cause of #3488004.)
- It blocks saving translations. The phantom item makes the entity differ from storage in the source language, so ContentEntityBase::hasTranslationChanges() returns TRUE. With content_translation's "Hide non translatable fields on translation forms" enabled, core's EntityUntranslatableFieldsConstraint rejects the save with:
Non-translatable fields can only be changed when updating the original language.
The editor sees this having edited only translatable fields, and the markup field is never the field named in the error, which makes it very hard to trace.
Steps to reproduce
- Install markup, language and content_translation, and add a second language.
- At admin/config/regional/content-language, enable translation for Article and tick "Hide non translatable fields on translation forms".
- Create and save an English Article.
- Now add a Markup field to Article — leave it untranslatable, and give it some markup text.
- Translate the article from step 3, change only the title, and save.
- Result: "Non-translatable fields can only be changed when updating the original language."
- Expected: the translation saves.
The step order matters: the article must exist before the field, so its stored revision has no row for it. That's the normal situation when adding a field to a live site.
The "Hide non translatable fields" setting is also required — it's what puts the bundle into untranslatable_fields.default_translation_affected mode, which makes core run the check on ordinary saves rather than only on pending revisions.
Proposed resolution
Report on the item's own value:
public function isEmpty() {
return $this->markup === NULL || $this->markup === '';
}
MarkupItemList::isEmpty() should be left unchanged. It contains the same settings-based check, but there it's correct and load-bearing: it's what makes the field render when it has no stored values. Changing both makes the configured markup disappear.
Verified on Drupal 11.4.4 / markup 2.0.1: the help text still renders, the item list is empty (count() === 0) while MarkupItemList::isEmpty() still returns FALSE, and the translation saves.
Remaining tasks
User interface changes
API changes
Data model changes
Issue fork markup-3615221
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 #3
ltrainComment #4
ltrain