Problem/Motivation

I’ve been searching for something else when I’ve seen content_moderation_entity_bundle_field_info(). I think that there might be a potential bug, because it does not clone the base field definition when setting the target bundle. I think that this might lead to overriding the target bundle setting for every bundle, because you’re re-using the reference to the original base field and not creating an unique object for each bundle.

Requesting the bundle fields for multiple bundles of the same entity type in a single request should demonstrate the problem.

An example for the correct implementation is \Drupal\taxonomy\Entity\Term::bundleFieldDefinitions().

Proposed resolution

Would it be possible to switch to the new FieldDefinition class - https://www.drupal.org/node/2982512 - and pass the base field definition to \Drupal\Core\Field\FieldDefinition::createFromFieldStorageDefinition()? This way we would not need to clone the base field anymore? I guess that we’ll also need an updated or a new CR describing this. Also to ensure no similar bugs occur we might start requiring from Drupal 9 that all objects returned by hook_entity_bundle_field_info() implementations are an instance of FieldDefinition and not BaseFieldDefinition.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#11 3077876-11.patch4.09 KBsam152
#9 3077876-9.patch1.74 KBsam152
#2 3077876-2.patch1.23 KBsam152

Issue fork drupal-3077876

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

hchonov created an issue. See original summary.

sam152’s picture

StatusFileSize
new1.23 KB

I've looked into this. I think you're right about misuse of the API, however I don't think I can reproduce this into an actionable bug. In EntityFieldManager::getFieldDefinitions, the provider is overridden each time you request bundle fields and he field definitions are then serialised and retrieved from the cache, creating new objects.

Attaching a patch, but no test since I can't reproduce a bug.

Status: Needs review » Needs work

The last submitted patch, 2: 3077876-2.patch, failed testing. View results

hchonov’s picture

Yes, they get serialized and the bug can be reproduced only if you retrieve the bundle fields for two different bundles in the same request.

I would expect the following to fail the assertion:

$entity_field_manager->clearCachedDefinitions();
$fields_a = $entity_field_manager->getFieldDefinitions($entity_type_id, $bundle_a);
$fields_b = $entity_field_manager->getFieldDefinitions($entity_type_id, $bundle_b);
$this->assertNotEqual($fields_a['moderation_state']->getTargetBundle(), $fields_b['moderation_state']->getTargetBundle());

P.S.:
I've just seen that the target bundle is being set automatically for the fields returned by the hook implementations entity_bundle_field_info. Therefore it should be enough just to return the field from the hook. From \Drupal\Core\Entity\EntityFieldManager::buildBundleFieldDefinitions():

    // Automatically set the field name, target entity type and bundle
    // for non-configurable fields.
    foreach ($bundle_field_definitions as $field_name => $field_definition) {
      if ($field_definition instanceof BaseFieldDefinition) {
        $field_definition->setName($field_name);
        $field_definition->setTargetEntityTypeId($entity_type_id);
      }
      if ($field_definition instanceof BaseFieldDefinition || $field_definition instanceof FieldDefinition) {
        $field_definition->setTargetBundle($bundle);
      }
    }
sam152’s picture

The sample test code you've given would pass, because like you said getFieldDefinitions sets the target bundle.

I'm also fine with setting the target bundle twice and being more explicit about intentions.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

sam152’s picture

Status: Needs work » Closed (works as designed)

Revisiting this, did we agree it's "works as designed", given it's not possible to trigger this bug?

hchonov’s picture

Status: Closed (works as designed) » Needs work

The sample test code you've given would pass, because like you said getFieldDefinitions sets the target bundle.

No, it will not because the second one will set the bundle on the same object.

I still think that the steps to reproduce in #4 should be sufficient to reproduce the bug.

sam152’s picture

StatusFileSize
new1.74 KB

Right, thanks for clarifying. I was indeed able to reproduce this in a test case. Still not sure on the fails from #2 though, haven't had a chance to look into them.

sam152’s picture

Status: Needs work » Needs review
sam152’s picture

StatusFileSize
new4.09 KB

This fixes a bunch of the fails, but I think the hook should be moved into EntityTypeInfo::class and cleaned up a bit.

The last submitted patch, 9: 3077876-9.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 11: 3077876-11.patch, failed testing. View results

hchonov’s picture

+++ b/core/modules/content_moderation/content_moderation.module
@@ -79,9 +81,29 @@ function content_moderation_entity_bundle_field_info(EntityTypeInterface $entity
+    $definition = FieldDefinition::createFromFieldStorageDefinition($base_field_definitions['moderation_state'])
+      ->setTargetBundle($bundle)
+      ->setLabel(t('Moderation state'))
+      ->setDescription(t('The moderation state of this piece of content.'))
+      ->setComputed(TRUE)
+      ->setClass(ModerationStateFieldItemList::class)
+      ->setDisplayOptions('view', [
+        'label' => 'hidden',
+        'region' => 'hidden',
+        'weight' => -5,
+      ])
+      ->setDisplayOptions('form', [
+        'type' => 'moderation_state_default',
+        'weight' => 100,
+        'settings' => [],
+      ])
+      ->addConstraint('ModerationState', [])
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayConfigurable('view', FALSE)
+      ->setReadOnly(FALSE)
+      ->setTranslatable(TRUE);

It would be great if we add a method on FieldDefinition for copying all that stuff from the base field definition, because it looks like this might be used a lot.

sam152’s picture

Looked into the fails, it seems since base fields support config overrides but plain field definitions do not, this breaks some low level stuff. Not really sure how to proceed.

This seems to work, but no idea if it makes sense conceptually:

diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php
index ebefa6121b..24ea687661 100644
--- a/core/lib/Drupal/Core/Field/FieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/FieldDefinition.php
@@ -270,6 +270,9 @@ public function getFieldStorageDefinition() {
    * {@inheritdoc}
    */
   public function getConfig($bundle) {
+    if ($this->getFieldStorageDefinition() instanceof FieldDefinitionInterface) {
+      return $this->getFieldStorageDefinition()->getConfig($bundle);
+    }
     // @todo provide a FieldDefinitionOverride config entity in
     // https://www.drupal.org/project/drupal/issues/2935978.
     throw new \Exception('Field definitions do not currently have an override config entity.');
hchonov’s picture

   public function getConfig($bundle) {
+    if ($this->getFieldStorageDefinition() instanceof FieldDefinitionInterface) {
+      return $this->getFieldStorageDefinition()->getConfig($bundle);
+    }

That will return the BaseField override as it will invoke \Drupal\Core\Field\BaseFieldDefinition::getConfig(), right?

In the hook we create the FieldDefinition out of the BaseFieldDefinition:
+ $definition = FieldDefinition::createFromFieldStorageDefinition($base_field_definitions['moderation_state']).

Therefore I think that it might make sense to return the base field override in FieldDefinition::getConfig() if the field storage definition is a base field. I am only not sure if we should it general as you are proposing or limit it to instances of BaseFieldDefinition.

sam152’s picture

Yeah, that may need a dedicated issue, I'm not sure. It's probably an overarching consideration of #3085023: [meta] Bundle field DX.

hchonov’s picture

Yeah, that may need a dedicated issue, I'm not sure.

A dedicated issue would be better.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

dww’s picture

Issue tags: +Bug Smash Initiative

I believe I hit exactly this bug at #3150294: New translations for moderated nodes are not created in the initial workflow state. For a while, there was a @todo comment in the code about it and a weird work-around (added in comment #27). I came up with a more elegant solution there (see comment #58), but this is still a bug. Thankfully, @Sam152 pointed me here in Slack, so I didn't open a duplicate followup for it. 😉 Tagging for Bug Smash. After 9.3.0-alpha1 I'll see if I can find some time to help move this forward.

Thanks,
-Derek

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Taran2L made their first commit to this issue’s fork.

taran2l’s picture

hello, run into the same issue while working on #3129874: The "moderation_state" base field overrides cause install from existing config to fail. The idea is to move to bundle fields (as moderation_state is per bundle) ... and it kinda works (one failing test is due to change in the test entity ... )

But, I think the whole Bundle Fields API portion is ... not finished, and while it works from the UI, but config import/export does not work

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.