When you add a new field to a content type and export configuration (via something like drush cex), the configuration for the teaser display mode is typically lacking a reference to the field you just created. The configuration for the teaser display mode will only get updated later once the display mode is actually used.
Steps to reproduce:
- Start with a fresh install of Drupal (I'm using Lightning). Run drush cex to capture a baseline configuration.
- Create a new content type foo and add a field to that content type (I think any field will do, but I used an entity reference)
- Run drush cex to export the new content type, field, and display modes.
- Open the config file core.entity_view_display.node.foo.teaser
Expected result:
The config file for the teaser display mode contains the field you just created under the "hidden" key.
Actual result:
The config file for the teaser display mode contains no reference to any field other than the default body field.
If you then go edit and save the teaser display mode in the UI, OR re-install the site from scratch using drush si --config-dir, and then re-export configuration, you'll note that the teaser display mode now properly contains the new field.
This may sound like a minor problem, but it actually wreaks havoc with a good configuration management / CI system, because a developer will typically add fields to a content type and capture the changes in config, thinking everything is fine, and then only much later in seemingly unrelated operations will the changes to the display mode suddenly come through. It also causes problems if you are attempting to verify configuration integrity (the way that BLT does), because this configuration will randomly show up later on and cause configuration integrity validation to fail.
Comment | File | Size | Author |
---|---|---|---|
#83 | 2915036-83.patch | 11.9 KB | Lendude |
#83 | interdiff-2915036-79-83.txt | 6.93 KB | Lendude |
#81 | 2915036-81.patch | 12.04 KB | koppie |
#79 | 2915036-79.patch | 11.7 KB | jofitz |
#79 | interdiff-2915036-78-79.txt | 1.67 KB | jofitz |
Comments
Comment #2
Dane Powell CreditAttribution: Dane Powell at Acquia commentedThis patch saves all active view modes whenever you add a new field, so that their respective configurations are updated. Normally the field simply gets added to the hidden region, although I suppose in the future folks might want to hook into this somehow to add fields to other regions.
Comment #3
Dane Powell CreditAttribution: Dane Powell at Acquia commentedMinor typo
Comment #4
tim.plunkettInstead of doing this via the UI, I think it should be a hook_ENTITY_TYPE_insert() implementation.
It would likely be
system_field_storage_config_insert()
, since #2551893: Add events for matching entity hooks still hasn't happened.Additionally, I think this should re-save both the EntityViewDisplay and EntityFormDisplay? Without checking, I believe the form would suffer the same problem.
Comment #5
Dane Powell CreditAttribution: Dane Powell at Acquia commentedTrue, this could be a problem for form display modes too (for those entity types that support them, i.e. not nodes). We've just never seen it because it's rare to have multiple form display modes.
I agree that we should solve this for both form and view display modes, and for all entity types. I'll take a shot at re-rolling the patch.
Comment #6
Dane Powell CreditAttribution: Dane Powell at Acquia commentedOkay, this patch takes the approach you suggested. It implements hook_ENTITY_TYPE_insert and hook_ENTITY_TYPE_update for field_storage_config in order to update both view and form display modes whenever a field is inserted or updated.
I actually think we only need to act on inserts, not updates, but this was necessary in order to work around a small problem:
$field_storage->getBundles()
returns null when called from field_field_storage_config_insert(). It seems that the only way to get the bundles associated with the new field storage config is to check later during field_field_storage_config_update(). If you know how to solve that, I'm happy to refactor this.Comment #8
Dane Powell CreditAttribution: Dane Powell at Acquia commentedThis should fix the test errors.
I'd still love any insight into why we can't handle everything in hook_ENTITY_TYPE_insert (see comment above).
Comment #10
Dane Powell CreditAttribution: Dane Powell at Acquia commentedI figured out half the problem... we should be acting on field inserts, not field storage inserts. The field storage is created before the field is, and the storage is obviously not attached directly to any bundles, so it's not possible for us to update display modes for bundles until the field is created.
Still, this fails tests due to this error:
My best guess is because the display mode configuration depends on the field configuration, and the field is not 100% saved at this point or something.
@tim.plunkett can you suggest any way to fix the failing tests?
Comment #11
tim.plunkettThe concern here is with updating existing displays, right? Not necessarily creating new ones every time.
entity_get_display() and entity_get_form_display() are both deprecated, because they combine load and create in an unclear way.
Switching to loading only fixes the fails for me.
Also switching away from the deprecated EntityManager.
This should be possible to write tests for.
Comment #12
Dane Powell CreditAttribution: Dane Powell at Acquia commentedAwesome, thanks!
I tried to write a test for this, but my test-fu is not strong. The field doesn't seem to be attached to the view mode ($display->getComponent('field_test') is always null), and I can't figure out why. This is what I have so far.
Comment #13
tim.plunkettIn the Issue Summary, you mention
This means it will never show up for ->getComponents(). We'd need to check under hidden.
So, I updated the test to do so.
However, running the test without the fix should fail. But for me it passes.
field_test
is already under hidden without the changes to field.module!Which means there's something missing between the Steps to Reproduce and the test as written.
Comment #14
ndf CreditAttribution: ndf at Dx Experts commentedAdded an issue that seems related.
There language settings where only updated in the config-exports after saving the displays one-by-one. Like here.
Comment #15
Dane Powell CreditAttribution: Dane Powell at Acquia commentedEntityViewDisplay::load() must recalculate the view mode on the fly to accommodate any new fields. The problem only occurs when you access the raw configuration, as you would when doing a configuration export. These patches should work.
Comment #17
tim.plunkettGood catch! Guessing it was \Drupal\Core\Entity\EntityDisplayBase::init() interfering.
Looks good to me, but someone else needs to sign-off.
Comment #18
borisson_This looks really good, and has test-coverage that fails when the fix isn't there.
Comment #19
ndf CreditAttribution: ndf at Dx Experts commentedIs this also working when a field is removed?
Comment #20
tim.plunkett\Drupal\Core\Entity\EntityDisplayBase::onDependencyRemoval() handles that case, which is much more essential to the runtime integrity of the site. This issue is more about maintaining the true state of the config at all times, and wouldn't affect runtime at all.
Comment #21
Dane Powell CreditAttribution: Dane Powell at Acquia commentedI've also verified empirically that display modes are correctly updated when fields are removed.
Comment #22
ndf CreditAttribution: ndf at Dx Experts commentedOk thanks for replies tim.plunkett and Dane Powell
Also RTBC
Comment #23
xjmStuff that creates noise in config diffs is major typically, so promoting.
The added hook implementation doesn't explain why it's necessary; probably we should add a comment documenting that?
Comment #24
Dane Powell CreditAttribution: Dane Powell at Acquia commentedComment #25
ndf CreditAttribution: ndf at Dx Experts commentedPlaying around with this patch in a separate drush command. So I could deploy it on an existing code-base.
https://github.com/DxEx/drupal8-drush-rebuild-displays
Noticed that the 'default' displays are missing now. I guess not on purpose.
Can we add these display to the $view_modes and $form_modes arrays?
Comment #26
Dane Powell CreditAttribution: Dane Powell at Acquia commentedI don't know what you mean by this. Are you saying that with this patch applied on a vanilla Drupal install, when you add a new field it's not added to the default display mode? This would be surprising, since I've tested this use case extensively. Can you provide steps to reproduce whatever it is that you're seeing?
Default displays already get updated whenever you modify fields (before this patch). The original bug report here is only related to ancillary displays (teasers, etc--see the steps to reproduce). This patch only affect these ancillary display modes, it doesn't touch the default displays.
Comment #27
Dane Powell CreditAttribution: Dane Powell at Acquia commentedTim, xjm, ndf, would one of you mind reviewing the comments I added in #24? I think this is ready to merge as far as most of us are concerned.
Comment #28
ndf CreditAttribution: ndf at Dx Experts commented#26 sorry for the delayed reply Dane Powell
Code looks solid.
Now it's called
hook_ENTITY_TYPE_insert
Shouldn't this also run on _delete and _update?
Do you mind to add test coverage for removing and updating?
And maybe also for the 'default' key.
Then everything is covered.
Comment #29
Dane Powell CreditAttribution: Dane Powell at Acquia commentedSee comments #19 and #20: the case of deleting is already handled by \Drupal\Core\Entity\EntityDisplayBase::onDependencyRemoval(), and updating should never result in needing this kind of update, because the conflict only comes with a field is added or removed. I can try to add test cases for these regardless, but I can assure you that they aren't necessary and would probably duplicate existing test cases (I'm sure onDependencyRemoval already has test coverage).
Comment #30
Dane Powell CreditAttribution: Dane Powell at Acquia commentedThis patch adds a test for removing a field and ensuring that it also gets removed from display modes, as well as a test for default display modes.
A test for updating fields would be useless, AFAIK, because updating a field doesn't update any of the configuration objects that we're worried about.
This should really be ready to merge now.
Comment #31
ndf CreditAttribution: ndf at Dx Experts commentedI have tested this patch and it works as expected.
Personally I prefer the extended tests from #30 so thanks!
But now I also tested #2680939: Display configuration not fully removed after uninstalling Language module with and without this patch. This patch doesn't solve that issue.
The interesting thing is that this patch does contain the code that fixes that issue!
Uninstalling
language
module does not triggerfield_field_config_insert()<code> (nor <code>field_field_config_update
andfield_field_config_remove
)When I run the code inside
field_field_config_insert()
for all entities, the one I published in #25, #2680939: Display configuration not fully removed after uninstalling Language module is fixed though!Thinking about that I want to propose a refactor of this patch:
- Move the executable part (inside
field_field_config_insert()
) to a service so that not onlyfield_field_config_insert()
can call it, but alsolanguage_uninstall()
and other functions/modules that want to rebuild the displays.- I am not sure if this we should optimize the code to do rebuild the displays per 'field' (as in the current patch). It could be ok to just rebuild all displays of all entities.
Comment #32
ndf CreditAttribution: ndf at Dx Experts commentedRewrote #30 to use a service.
This way #2680939: Display configuration not fully removed after uninstalling Language module can be fixed with the same service.
In the executable code (that is in the service) 2 changes are made:
- The 'default' display is added to all bundles. This is done because those were not parsed when uninstalling language module (#2680939)
- The 'status' for display check is removed. This is done because
core.entity_view_display.node.article.rss
was skipped when uninstalling language module (#2680939). This change seems harmless because there is a NULL check already.Hereby 3 patches:
1. Failing test only
2. Test + service patch for this issue only
3. Combined patch with #2680939
The interdiff is a not so useful but also added
Comment #34
ndf CreditAttribution: ndf at Dx Experts commentedLet's try the combined patch again. It validates locally. Removed the # from the filename.
Comment #38
ndf CreditAttribution: ndf at Dx Experts commentedA javascript test is failing (Field.Drupal\field\Tests\FieldImportDeleteUninstallUiTest) that is not in this patch.
That test does things with fields, so that suspicious.
#2680939: Display configuration not fully removed after uninstalling Language module went green though.
Maybe the changes with 'default' display and 'status' parsing are the reason.
I must dive in to it. But that will take a few days. Especially because my javascript tests are not running locally.
For now I re-queued the tests here, maybe it is the test-bot.
Comment #39
ndf CreditAttribution: ndf at Dx Experts commentedRan some variations of #34 patch on #2274167: [ignore] Patch testing issue
The default-display part breaks
core/modules/field/src/Tests/FieldImportDeleteUninstallUiTest.php
Analysis so far: test
FieldImportDeleteUninstallUiTest
invokesfield_field_config_insert()
and that triggers saving a default display that fails the test.
Comment #40
ndf CreditAttribution: ndf at Dx Experts commentedAlso added this patch to #2680939: Display configuration not fully removed after uninstalling Language module
#39 was triggered by updating the default-form-display in
field_field_config_insert()
.Solved this by adding a parameter
$include_default_displays
to the service.Now both #2680939: Display configuration not fully removed after uninstalling Language module and this issue should be green!
Comment #42
ndf CreditAttribution: ndf at Dx Experts commentedNeeds review.
Both
drupal-2915036-40--service-approach.patch
anddrupal-2915036-40--service-approach--and-patch-2680939-15--combined.patch
are green yay!Also #2680939: Display configuration not fully removed after uninstalling Language module#15 is green!
Comment #43
Dane Powell CreditAttribution: Dane Powell at Acquia commented#40 looks good
Comment #46
ndf CreditAttribution: ndf at Dx Experts commentedTest bot doesn't like the interdiff. Re-uploading #40 and re-scheduling the tests.
Comment #47
larowlanWe don't reference issues in @see comments. Can you remove this.
Can you put the comment inside the function body, so the docblock should just contain the
Implements hook_ENTITY_TYPE_insert() for 'field_config'
There's a mismatch here
There are no calls to this, and no test coverage.
Let's just remove it
nit blank link isn't needed
entity_get_bundles is deprecated, let's inject the entity type bundle info service and use that instead
These don't need to be inside the
foreach ($entity_bundles) ...
loopCan we use array_map here to create an array of ids and then use load multiple.
e.g
That way we can load them all in one go, instead one by one.
We could even make the callback in array_map a closure and reuse it for both form and view modes
Also, we have the entityTypeManager injected, so lets use it instead of the static ::load method
These are the same? Shouldn't one be .default?
I assume this already exists in core because we're not doing anything for hook_entity_ENTITY_TYPE_delete in this patch?
If so is this test coverage unneeded?
Comment #48
ndf CreditAttribution: ndf at Dx Experts commentedThanks for the review larowlan
#47.3 Because of #2680939: Display configuration not fully removed after uninstalling Language module. I remove it in this patch. First things first.
#47.8 Missed this one!
#47.9 Dane Powell you were right. I was asking for this because of #2680939.
Others: will do. Expect a week or so.
Comment #49
ndf CreditAttribution: ndf at Dx Experts commented47.1
Done
47.2
Done, see 47.5
47.3
Removed. Must be reintroduced in #2680939: Display configuration not fully removed after uninstalling Language module
47.4
Is this the last line?
Did not change anything.
I thought every php-file must end with on blank line.
47.5
Done
47.6
Done
47.7
done: arrap_map and loadMultiple()
done: use entityTypeManager
the closure thing I did not.
48.8
Extended the test to now include
view-mode default
view-mode foobar
form-mode default
form-mode foobar
Regarding the view-modes I found an a strange assumption in entity_test_entity_form_mode_info_alter().
I changed that _alter to add form-mode 'compact', without removing other form-mode. In this patch there is test-coverage for default and foobar.
48.9
Removed these tests.
I also validated this manually.
Comment #50
Dane Powell CreditAttribution: Dane Powell at Acquia commentedlarowlan, I think you're in the best position to review this since you requested changes, can you please review? We _really_ need to get this committed, we are burning countless dev hours constantly hitting this bug.
Comment #51
LendudeBeen looking at some things with @ndf
if we use getViewModeOptions and getFormModeOptions, this would allow us to get rid of the $include_default_displays parameters, which makes this much easier to understand.
Comment #52
ndf CreditAttribution: ndf at Dx Experts commentedThanks lendude, definitely an improvement.
Updated the patch + interdiff with #49
Also updated the combined patch in #2680939: Display configuration not fully removed after uninstalling Language module
Comment #54
ndf CreditAttribution: ndf at Dx Experts commented#52 failed because of
core/modules/field/src/Tests/FieldImportDeleteUninstallUiTest.php
with message#52 enforces creating of 'default' displays when a new field is created. The above test must remove that to run successful.
Comment #55
LendudeIt would be nice to get rid of these hardcoded names and just use getConfigDependencyName() instead.
So store the display name in a var and then use that, so something like:
And then just
$this->assertArrayNotHasKey('field_test', \Drupal::config($this->defaultViewDisplayName)->get('hidden'));
Comment #57
anavarreComment #58
lcatlett CreditAttribution: lcatlett commentedI have resolved this and other related issues by overriding the default service container cache backends since drupal bootstraps the container to the database. I believe this is needed when updating the data, discovery, and config bins when deploying updates such as this to display mode configurations since the field storage needs to update at runtime.
See https://github.com/acquia/blt/blob/9.1.x/settings/memcache.settings.php for an example with memcache.
Comment #59
ndf CreditAttribution: ndf at Dx Experts commented#58 Can you explain why the cache-backend is relevant here?
No one mentioned anything regarding caching nor the service-container to reproduce this issue.
For the record: In my local development testing I only used database-backend.
Comment #60
Dane Powell CreditAttribution: Dane Powell at Acquia commentedHere is #54 re-rolled with the feedback from #55.
Comment #62
Dane Powell CreditAttribution: Dane Powell at Acquia commentedI think we should still target 8.5.x for this since it's a major bug.
Also I'm at a loss to explain the test failure in #60. Does anyone else understand what's causing that?
Comment #63
ndf CreditAttribution: ndf at Dx Experts commentedThe failed test is introduced in 8.5.x #2940201: hook_field_widget_form_alter() can no longer affect the whole widget for multi-value fields
There is also a follow up, not sure if it is related #2943035: hook_field_widget_multivalue_form_alter and hook_field_widget_multivalue_WIDGET_TYPE_alter are not invoked for single widget that handle multiple values
While upgrading my local test environment to 8.5.x I broke it, not the first time 😬
So currently no xdebug in my test-suite.
One guess is that this part in the #2940201: hook_field_widget_form_alter() can no longer affect the whole widget for multi-value fields test
might give troubles. This will trigger the new $display_rebuilder service introduced here.
Comment #64
ndf CreditAttribution: ndf at Dx Experts commentedJust rescheduled the last passing patch (#54) against:
-8.4.x
-8.5.x
-8.6.x
Comment #66
ndf CreditAttribution: ndf at Dx Experts commentedYes it the new
widgetAlterTest
.Now branch 8.5 and 8.6 are failing.
Tried to understand the changes #2940201: hook_field_widget_form_alter() can no longer affect the whole widget for multi-value fields but I don't get the relation with this issue.
Comment #67
ndf CreditAttribution: ndf at Dx Experts commentedtest
testFieldFormMultipleWidgetTypeAlterSingleValues
was also changed in #2943035: hook_field_widget_multivalue_form_alter and hook_field_widget_multivalue_WIDGET_TYPE_alter are not invoked for single widget that handle multiple valuesSo first #2940201: hook_field_widget_form_alter() can no longer affect the whole widget for multi-value fields
then #2943035: hook_field_widget_multivalue_form_alter and hook_field_widget_multivalue_WIDGET_TYPE_alter are not invoked for single widget that handle multiple values
#2943189: Clean up the test code for hook_field_widget_multivalue_form_alter() is still open, but that doesn't solve this issue.
Diving in this is the line that triggers the failing test:
$this->entityTypeManager->getStorage('entity_form_display')->loadMultiple($form_mode_ids);
That line is inside
rebuildEntityTypeDisplays()
Note that this is only loading of data, nothing is saved here!
I also tried to revert the change with form-mode 'compact', but that doesn't make any difference.
It might be a state-thing inside the
testFieldFormMultipleWidgetTypeAlterSingleValues
test. see #58Currently my gut-feeling is that this issue is only exposing an issue in the new test and/or hook.
Comment #68
LendudeThe problem in #60 seems to be that when loadMultiple is called, all hook information gets cached. So after updating the component through the API, the hook cache has no idea that it needs to be refreshed. So we need to force it to refresh.
Using
$this->rebuildAll();
might be a bit more then is needed but its mostly to show where the problem lies with this failing test.So this problem stems mostly from
\Drupal\field\Tests\FormTest
doing API calls in functional tests which can cause the container and/or cache to be out of sync.Comment #69
Dane Powell CreditAttribution: Dane Powell at Acquia commented#68 looks good, fixes the tests, and fixes the original bug.
Comment #71
tacituseu CreditAttribution: tacituseu commentedUnrelated failure (DST).
Comment #72
ndf CreditAttribution: ndf at Dx Experts commentedThese changes are fixing test-issues exposed by this patch. Seems ok to fix here.
RTBC from me too.
Comment #73
larowlanIn the context of inserting a new field config entity, we have a bundle in scope.
Yet the display rebuilder does not take this into account, and instead rebuilds all form and view modes for all bundles of the given entity type.
Shouldn't we limit it to the bundle of the field being inserted?
Could we get an extended comment here explaining a bit about where this is used with an @see reference to the field config insert hook where it is used from.
The current short comment is a bit light on.
Can we use less of a sledgehammer approach here? Can we clear just the entity or plugin caches that are stale?
That looks like a c/p error?
These need docblocks right?
Can we get a follow-up to add a test-trait for creating these, see it a lot, would be a useful utility
unrelated?
Comment #74
ndf CreditAttribution: ndf at Dx Experts commented#73.1
Sounds sane to limit rebuildEntityTypeDisplays() with a bundle parameter.
But I have to dive in to it. Up till #30 the resaving was limited by bundle.
And I removed that option when refactoring to the service-approach.
Maybe this was done for #2680939: Display configuration not fully removed after uninstalling Language module
#73.7
This change is intentional.
Form mode display tests were overridden by the original form_mode_info_alter() because it drops all form-modes and replaces this with only 'compact'. Therefore this change where form_mode_info_alter() adds 'compact' to array for form-modes instead of replacing it.
Comment #76
Dane Powell CreditAttribution: Dane Powell at Acquia commentedWe need a re-roll for 8.6
Comment #77
anavarreComment #78
jofitz CreditAttribution: jofitz at ComputerMinds commentedRe-rolled, but still needs to address comments in #73.
Comment #79
jofitz CreditAttribution: jofitz at ComputerMinds commentedInclude a couple of changes that I managed to miss in the previous patch.
Still need to address comments in #73, but let's get this re-roll correct first.
Comment #80
dabbor CreditAttribution: dabbor commentedWe need a re-roll for 8.6 too.
Does anybody have an advice how to make the patch working for Drupal 8.6?
Comment #81
koppie CreditAttribution: koppie as a volunteer commentedHere's a patch for 8.6.4.
Comment #83
LendudeReroll and addresses #73 and does some clean up. Changed the service to a helper class since there should be no need to swap this out and we shouldn't make this API
#73.1 done
#73.2 done
#73.3 *shrug* it's a test and don't think we have any Testing API's to do this in a subtle manner, left this
#73.4 changed
#73.5 yup, added
#73.6 not sure, yeah its used fairly often but not all that hard to set up now, less API to maintain like this *shrug*
#73.7 see #74, this change is needed
Comment #84
ndf CreditAttribution: ndf at Dx Experts commentedThanks Lendude for picking this up. Patch looks good to me.
I have a question regarding #73.1 and changing the service to a helper class:
In issue #2680939: Display configuration not fully removed after uninstalling Language module we want to use the
EntityDisplayRebuilder
introduced in this patch.Is it still possible? Or is a service required then?
Comment #85
Lendude@ndf since all the calls there are also from hooks, it can just use it in the same way as this patch by calling
\Drupal::classResolver(EntityDisplayRebuilder::class)->rebuildEntityTypeDisplays($field->getTargetEntityTypeId(), $field->getTargetBundle());
Comment #86
ndf CreditAttribution: ndf at Dx Experts commentedAll feedback has been addressed! So RTBC.
Comment #87
badrange CreditAttribution: badrange at Digia commentedThis issue has been RTBC for one and a half months, is there anything that prevents it from being committed?
Comment #89
LendudeUnrelated fail, back to RTBC
Comment #95
larowlanCan we get a follow-up issue to explore if we can add some testing API for this.
Adding issue credits, adding myself for review in #73
Also adding @sime, @Joseph Zhao, @fubarhouse and @Alex Skrypnyk who sat in on a one hour review of this patch as part of core process walkthrough
Comment #99
larowlanCommitted 49d60f1 and pushed to 9.0.x. Thanks!
c/p to 8.9.x
As this is a major bug with no disruption, I feel this is eligble for backport to 8.8.x, so c/p to 8.8.x too.
Comment #101
MrPaulDriver CreditAttribution: MrPaulDriver commentedWondering if this will find it's way into non-dev 8.8 release?