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
| Comment | File | Size | Author |
|---|---|---|---|
| #11 | 3077876-11.patch | 4.09 KB | sam152 |
| #9 | 3077876-9.patch | 1.74 KB | sam152 |
| #2 | 3077876-2.patch | 1.23 KB | sam152 |
Issue fork drupal-3077876
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
sam152 commentedI'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.
Comment #4
hchonovYes, 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:
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():Comment #5
sam152 commentedThe sample test code you've given would pass, because like you said
getFieldDefinitionssets the target bundle.I'm also fine with setting the target bundle twice and being more explicit about intentions.
Comment #7
sam152 commentedRevisiting this, did we agree it's "works as designed", given it's not possible to trigger this bug?
Comment #8
hchonovNo, 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.
Comment #9
sam152 commentedRight, 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.
Comment #10
sam152 commentedComment #11
sam152 commentedThis fixes a bunch of the fails, but I think the hook should be moved into
EntityTypeInfo::classand cleaned up a bit.Comment #14
hchonovIt 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.
Comment #15
sam152 commentedLooked 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:
Comment #16
hchonovThat 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.Comment #17
sam152 commentedYeah, that may need a dedicated issue, I'm not sure. It's probably an overarching consideration of #3085023: [meta] Bundle field DX.
Comment #18
hchonovA dedicated issue would be better.
Comment #22
dwwI 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
Comment #28
taran2lhello, 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