Issue #2915036 by ndf, Dane Powell, tim.plunkett, Lendude, jofitz, koppie, larowlan, fubarhouse, sime, alex.skrypnyk, Joseph Zhao: Display mode configurations don't get updated with new fields

merge-requests/2419/head
Lee Rowlands 2019-10-30 15:36:20 +10:00
parent 608c6bc8fa
commit 49d60f113d
No known key found for this signature in database
GPG Key ID: 2B829A3DF9204DC4
6 changed files with 262 additions and 6 deletions

View File

@ -11,6 +11,7 @@ use Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface;
use Drupal\field\ConfigImporterFieldPurger;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\EntityDisplayRebuilder;
use Drupal\field\FieldConfigInterface;
use Drupal\field\FieldStorageConfigInterface;
use Drupal\Core\Form\FormStateInterface;
@ -335,6 +336,22 @@ 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.
\Drupal::classResolver(EntityDisplayRebuilder::class)
->rebuildEntityTypeDisplays($field->getTargetEntityTypeId(), $field->getTargetBundle());
}
/**
* Implements hook_ENTITY_TYPE_update() for 'field_storage_config'.
*

View File

@ -0,0 +1,98 @@
<?php
namespace Drupal\field;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Rebuilds all form and view modes for a passed entity bundle.
*
* @see field_field_config_insert()
*
* @internal
*/
class EntityDisplayRebuilder implements ContainerInjectionInterface {
/**
* The field storage config storage.
*
* @var \Drupal\Core\Entity\EntityTypeManager
*/
protected $entityTypeManager;
/**
* The display repository.
*
* @var \Drupal\Core\Entity\EntityDisplayRepository
*/
protected $entityDisplayRepository;
/**
* The entity type bundle info.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* Constructs a new EntityDisplayRebuilder.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager.
* @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
* The entity display repository.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
$this->entityTypeManager = $entity_type_manager;
$this->entityDisplayRepository = $entity_display_repository;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('entity_display.repository'),
$container->get('entity_type.bundle.info')
);
}
/**
* Rebuild displays for single Entity Type.
*
* @param string $entity_type_id
* The entity type machine name.
*
* @param string $bundle
* The bundle we need to rebuild.
*/
public function rebuildEntityTypeDisplays($entity_type_id, $bundle) {
// Get the displays.
$view_modes = $this->entityDisplayRepository->getViewModeOptions($entity_type_id);
$form_modes = $this->entityDisplayRepository->getFormModeOptions($entity_type_id);
// 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();
}
}
}

View File

@ -86,9 +86,10 @@ class FieldImportDeleteUninstallUiTest extends FieldTestBase {
unset($core_extension['module']['telephone']);
$sync->write('core.extension', $core_extension);
// Stage the field deletion
// Stage the field deletion including its dependencies.
$sync->delete('field.storage.entity_test.field_tel');
$sync->delete('field.field.entity_test.entity_test.field_tel');
$sync->delete('core.entity_form_display.entity_test.entity_test.default');
$this->drupalGet('admin/config/development/configuration');
// Test that the message for one field being purged during a configuration
// synchronization is correct.

View File

@ -713,6 +713,10 @@ class FormTest extends FieldTestBase {
])
->save();
// We need to rebuild hook information after setting the component through
// the API.
$this->rebuildAll();
$this->drupalGet('entity_test/add');
$this->assertUniqueText("From $hook(): prefix on $field_name parent element.");
if ($widget === 'test_field_widget_multiple_single_value') {

View File

@ -0,0 +1,138 @@
<?php
namespace Drupal\Tests\field\Kernel;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityFormMode;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Ensure display modes are updated when fields are created.
*
* @group field
*/
class DisplayModeUpdateTest extends FieldKernelTestBase {
/**
* The default view display name.
*
* @var string
*/
protected $defaultViewDisplayName;
/**
* The default form display name.
*
* @var string
*/
protected $defaultFormDisplayName;
/**
* The alternate view display name.
*
* @var string
*/
protected $foobarViewDisplayName;
/**
* The alternate form display name.
*
* @var string
*/
protected $foobarFormDisplayName;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create 'default' view-display.
$default_view_display = EntityViewDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'default',
'status' => TRUE,
]);
$default_view_display->save();
$this->defaultViewDisplayName = $default_view_display->getConfigDependencyName();
// Create 'default' form-display.
$default_form_display = EntityFormDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'default',
'status' => TRUE,
]);
$default_form_display->save();
$this->defaultFormDisplayName = $default_form_display->getConfigDependencyName();
// 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();
$foobar_view_display = EntityViewDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'foobar',
'status' => TRUE,
]);
$foobar_view_display->save();
$this->foobarViewDisplayName = $foobar_view_display->getConfigDependencyName();
// 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();
$foobar_form_display = EntityFormDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'foobar',
'status' => TRUE,
]);
$foobar_form_display->save();
$this->foobarFormDisplayName = $foobar_form_display->getConfigDependencyName();
}
/**
* Ensure display modes are updated when fields are created.
*/
public function testDisplayModeUpdateAfterFieldCreation() {
// Sanity test: field has not been created yet, so should not exist in display.
$this->assertArrayNotHasKey('field_test', \Drupal::config($this->defaultViewDisplayName)->get('hidden'));
$this->assertArrayNotHasKey('field_test', \Drupal::config($this->defaultFormDisplayName)->get('hidden'));
$this->assertArrayNotHasKey('field_test', \Drupal::config($this->foobarViewDisplayName)->get('hidden'));
$this->assertArrayNotHasKey('field_test', \Drupal::config($this->foobarFormDisplayName)->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($this->defaultViewDisplayName)->get('hidden'));
$this->assertArrayHasKey('field_test', \Drupal::config($this->defaultFormDisplayName)->get('hidden'));
$this->assertArrayHasKey('field_test', \Drupal::config($this->foobarViewDisplayName)->get('hidden'));
$this->assertArrayHasKey('field_test', \Drupal::config($this->foobarFormDisplayName)->get('hidden'));
}
}

View File

@ -282,11 +282,9 @@ function entity_test_entity_form_mode_info_alter(&$form_modes) {
$entity_info = \Drupal::entityTypeManager()->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,
];
}
}