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
parent
608c6bc8fa
commit
49d60f113d
|
@ -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'.
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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'));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue