diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 14340b8b..18458a89 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -334,6 +334,23 @@ function field_form_config_admin_import_form_alter(&$form, FormStateInterface $f } } +/** + * Implements hook_ENTITY_TYPE_insert() for 'field_config'. + */ +function field_field_config_insert(FieldConfigInterface $field) { + if ($field->isSyncing()) { + // Don't change anything during a configuration sync. + return; + } + + // Allow other view modes to update their configuration for the new field. + // Otherwise, configuration for view modes won't get updated until the mode + // is used for the first time, creating noise in config diffs. + /** @var \Drupal\field\EntityDisplayRebuilder $display_rebuilder */ + $display_rebuilder = \Drupal::service('field.entity_display_rebuilder'); + $display_rebuilder->rebuildEntityTypeDisplays($field->getTargetEntityTypeId()); +} + /** * Implements hook_ENTITY_TYPE_update() for 'field_storage_config'. * diff --git a/core/modules/field/field.services.yml b/core/modules/field/field.services.yml index 321cf916..3ca8cfab 100644 --- a/core/modules/field/field.services.yml +++ b/core/modules/field/field.services.yml @@ -5,3 +5,6 @@ services: - { name: module_install.uninstall_validator } arguments: ['@entity_type.manager', '@string_translation', '@plugin.manager.field.field_type'] lazy: true + field.entity_display_rebuilder: + class: Drupal\field\EntityDisplayRebuilder + arguments: ['@entity_type.manager', '@entity_display.repository', '@entity_type.bundle.info'] diff --git a/core/modules/field/src/EntityDisplayRebuilder.php b/core/modules/field/src/EntityDisplayRebuilder.php new file mode 100644 index 00000000..b5675530 --- /dev/null +++ b/core/modules/field/src/EntityDisplayRebuilder.php @@ -0,0 +1,92 @@ +entityTypeManager = $entity_type_manager; + $this->entityDisplayRepository = $entity_display_repository; + $this->entityTypeBundleInfo = $entity_type_bundle_info; + } + + /** + * Rebuild displays for single Entity Type. + * + * @param string $entity_type_id + * The entity type machine name. + * @param bool $include_default_displays + * Add 'default' displays. Defaults to FALSE. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * In case of failures an exception is thrown. + */ + public function rebuildEntityTypeDisplays($entity_type_id, $include_default_displays = FALSE) { + + // Get the displays. + $view_modes = $this->entityDisplayRepository->getViewModes($entity_type_id); + $form_modes = $this->entityDisplayRepository->getFormModes($entity_type_id); + if ($include_default_displays) { + $view_modes['default'] = []; + $form_modes['default'] = []; + } + + // Loop through all bundles and save all displays. + $entity_bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id); + foreach ($entity_bundles as $bundle => $bundle_definition) { + // Save view mode displays. + $view_mode_ids = array_map(function ($view_mode) use ($entity_type_id, $bundle) { + return "$entity_type_id.$bundle.$view_mode"; + }, array_keys($view_modes)); + foreach ($this->entityTypeManager->getStorage('entity_view_display')->loadMultiple($view_mode_ids) as $display) { + $display->save(); + } + // Save form mode displays. + $form_mode_ids = array_map(function ($form_mode) use ($entity_type_id, $bundle) { + return "$entity_type_id.$bundle.$form_mode"; + }, array_keys($form_modes)); + foreach ($this->entityTypeManager->getStorage('entity_form_display')->loadMultiple($form_mode_ids) as $display) { + $display->save(); + } + } + } + +} diff --git a/core/modules/field/tests/src/Kernel/DisplayModeUpdateTest.php b/core/modules/field/tests/src/Kernel/DisplayModeUpdateTest.php new file mode 100644 index 00000000..4318e78d --- /dev/null +++ b/core/modules/field/tests/src/Kernel/DisplayModeUpdateTest.php @@ -0,0 +1,118 @@ + 'entity_test.default', + 'targetEntityType' => 'entity_test', + 'status' => TRUE, + 'enabled' => TRUE, + ])->save(); + EntityViewDisplay::create([ + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'mode' => 'default', + 'status' => TRUE, + ])->save(); + + // Create 'default' form-display. + EntityFormMode::create([ + 'id' => 'entity_test.default', + 'targetEntityType' => 'entity_test', + 'status' => TRUE, + 'enabled' => TRUE, + ])->save(); + EntityFormDisplay::create([ + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'mode' => 'default', + 'status' => TRUE, + ])->save(); + + // Create a view-mode 'foobar', create view-display that uses it. + EntityViewMode::create([ + 'id' => 'entity_test.foobar', + 'targetEntityType' => 'entity_test', + 'status' => TRUE, + 'enabled' => TRUE, + ])->save(); + EntityViewDisplay::create([ + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'mode' => 'foobar', + 'status' => TRUE, + ])->save(); + + // Create a new form-mode 'foobar', create form-display that uses it. + EntityFormMode::create([ + 'id' => 'entity_test.foobar', + 'targetEntityType' => 'entity_test', + 'status' => TRUE, + 'enabled' => TRUE, + ])->save(); + EntityFormDisplay::create([ + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'mode' => 'foobar', + 'status' => TRUE, + ])->save(); + } + + /** + * Ensure display modes are updated when fields are created. + */ + public function testDisplayModeUpdateAfterFieldCreation() { + $default_view_display = 'core.entity_view_display.entity_test.entity_test.default'; + $default_form_display = 'core.entity_form_display.entity_test.entity_test.default'; + $foobar_view_display = 'core.entity_view_display.entity_test.entity_test.foobar'; + $foobar_form_display = 'core.entity_form_display.entity_test.entity_test.foobar'; + + // Sanity test: field has not been created yet, so should not exist in display. + $this->assertArrayNotHasKey('field_test', \Drupal::config($default_view_display)->get('hidden')); + $this->assertArrayNotHasKey('field_test', \Drupal::config($default_form_display)->get('hidden')); + $this->assertArrayNotHasKey('field_test', \Drupal::config($foobar_view_display)->get('hidden')); + $this->assertArrayNotHasKey('field_test', \Drupal::config($foobar_form_display)->get('hidden')); + + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_test', + 'entity_type' => 'entity_test', + 'type' => 'test_field', + 'cardinality' => 1, + ]); + $field_storage->save(); + + FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => 'entity_test', + ])->save(); + + // Ensure field is added to display modes. + $this->assertArrayHasKey('field_test', \Drupal::config($default_view_display)->get('hidden')); + $this->assertArrayHasKey('field_test', \Drupal::config($default_form_display)->get('hidden')); + $this->assertArrayHasKey('field_test', \Drupal::config($foobar_view_display)->get('hidden')); + $this->assertArrayHasKey('field_test', \Drupal::config($foobar_form_display)->get('hidden')); + + } + +} diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index bcf55f48..c2887f41 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -231,11 +231,9 @@ function entity_test_entity_form_mode_info_alter(&$form_modes) { $entity_info = \Drupal::entityManager()->getDefinitions(); foreach ($entity_info as $entity_type => $info) { if ($entity_info[$entity_type]->getProvider() == 'entity_test') { - $form_modes[$entity_type] = [ - 'compact' => [ - 'label' => t('Compact version'), - 'status' => TRUE, - ], + $form_modes[$entity_type]['compact'] = [ + 'label' => t('Compact version'), + 'status' => TRUE, ]; } }