diff --git a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php index c67dc68..c851f14 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php +++ b/core/lib/Drupal/Core/Entity/Plugin/EntityReferenceSelection/SelectionBase.php @@ -111,7 +111,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta // Merge-in default values. $selection_handler_settings += array( - 'target_bundles' => array(), + 'target_bundles' => NULL, 'sort' => array( 'field' => '_none', ), @@ -128,7 +128,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#type' => 'checkboxes', '#title' => $this->t('Bundles'), '#options' => $bundle_options, - '#default_value' => (!empty($selection_handler_settings['target_bundles'])) ? $selection_handler_settings['target_bundles'] : array(), + '#default_value' => $selection_handler_settings['target_bundles'], '#required' => TRUE, '#size' => 6, '#multiple' => TRUE, @@ -207,7 +207,14 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta /** * {@inheritdoc} */ - public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { } + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + // Normalize the value of the 'target_bundles' setting. In case it is an + // empty array we need to change it to NULL in order to allow the field to + // reference all bundles of the target entity type. + if ($form_state->getValue(['settings', 'handler_settings', 'target_bundles']) === []) { + $form_state->setValue(['settings', 'handler_settings', 'target_bundles'], NULL); + } + } /** * {@inheritdoc} @@ -326,6 +333,15 @@ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') $entity_type = $this->entityManager->getDefinition($target_type); $query = $this->entityManager->getStorage($target_type)->getQuery(); + + // If the 'target_bundles' setting is an empty array, force the query to + // never return anything and bail out early. + if ($handler_settings['target_bundles'] === []) { + $query->condition($entity_type->getKey('id'), NULL, '='); + + return $query; + } + if (!empty($handler_settings['target_bundles'])) { $query->condition($entity_type->getKey('bundle'), $handler_settings['target_bundles'], 'IN'); } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php index 902b5c2..bd164a9 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php @@ -322,13 +322,13 @@ public static function calculateDependencies(FieldDefinitionInterface $field_def */ public static function onDependencyRemoval(FieldDefinitionInterface $field_definition, array $dependencies) { $changed = parent::calculateDependencies($field_definition, $dependencies); - $manager = \Drupal::entityManager(); - $target_entity_type = $manager->getDefinition($field_definition->getFieldStorageDefinition()->getSetting('target_type')); + $entity_manager = \Drupal::entityManager(); + $target_entity_type = $entity_manager->getDefinition($field_definition->getFieldStorageDefinition()->getSetting('target_type')); if ($default_value = $field_definition->getDefaultValueLiteral()) { foreach ($default_value as $key => $value) { if (is_array($value) && isset($value['target_uuid'])) { - $entity = $manager->loadEntityByUuid($target_entity_type->id(), $value['target_uuid']); + $entity = $entity_manager->loadEntityByUuid($target_entity_type->id(), $value['target_uuid']); // @see \Drupal\Core\Field\EntityReferenceFieldItemList::processDefaultValue() if ($entity && isset($dependencies[$entity->getConfigDependencyKey()][$entity->getConfigDependencyName()])) { unset($default_value[$key]); @@ -340,14 +340,15 @@ public static function onDependencyRemoval(FieldDefinitionInterface $field_defin $field_definition->setDefaultValue($default_value); } } + $bundles_changed = FALSE; - $handler = $field_definition->getSetting('handler_settings'); - if (!empty($handler['target_bundles'])) { + $handler_settings = $field_definition->getSetting('handler_settings'); + if (!empty($handler_settings['target_bundles'])) { if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) { - if ($storage = $manager->getStorage($bundle_entity_type_id)) { - foreach ($storage->loadMultiple($handler['target_bundles']) as $bundle) { + if ($storage = $entity_manager->getStorage($bundle_entity_type_id)) { + foreach ($storage->loadMultiple($handler_settings['target_bundles']) as $bundle) { if (isset($dependencies[$bundle->getConfigDependencyKey()][$bundle->getConfigDependencyName()])) { - unset($handler['target_bundles'][$bundle->id()]); + unset($handler_settings['target_bundles'][$bundle->id()]); $bundles_changed = TRUE; } } @@ -355,7 +356,7 @@ public static function onDependencyRemoval(FieldDefinitionInterface $field_defin } } if ($bundles_changed) { - $field_definition->setSetting('handler_settings', $handler); + $field_definition->setSetting('handler_settings', $handler_settings); } $changed |= $bundles_changed; diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module index a09a53c..889de4e 100644 --- a/core/modules/entity_reference/entity_reference.module +++ b/core/modules/entity_reference/entity_reference.module @@ -213,11 +213,9 @@ function entity_reference_query_entity_reference_alter(AlterableInterface $query /** * Implements hook_entity_bundle_rename(). - * - * @todo Move this in an event subscriber once - * https://www.drupal.org/node/2553169 is in. */ -function entity_reference_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) { +function entity_reference_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) { + // Gather a list of all entity reference fields. $map = \Drupal::entityManager()->getFieldMapByFieldType('entity_reference'); $ids = []; foreach ($map as $type => $info) { @@ -228,22 +226,57 @@ function entity_reference_entity_bundle_rename($entity_type, $bundle_old, $bundl } } - /** @var \Drupal\field\Entity\FieldConfig $config */ - foreach (FieldConfig::loadMultiple($ids) as $config) { - if ($config->getSetting('target_type') == $entity_type) { - $handler = $config->getSetting('handler_settings'); - $bundles = !empty($handler['target_bundles']) ? array_keys($handler['target_bundles']) : []; - foreach ($bundles as $bundle) { - if ($bundle == $bundle_old) { - $handler['target_bundles'][$bundle_new] = $bundle_new; - unset($handler['target_bundles'][$bundle_old]); - $config->setSetting('handler_settings', $handler); - $config->save(); - break; + // Update the 'target_bundles' handler setting if needed. + foreach (FieldConfig::loadMultiple($ids) as $field_config) { + if ($field_config->getSetting('target_type') == $entity_type_id) { + $handler_settings = $field_config->getSetting('handler_settings'); + if (isset($handler_settings['target_bundles'][$bundle_old])) { + unset($handler_settings['target_bundles'][$bundle_old]); + $handler_settings['target_bundles'][$bundle_new] = $bundle_new; + $field_config->setSetting('handler_settings', $handler_settings); + $field_config->save(); + } + } + } +} + +/** + * Implements hook_entity_bundle_delete(). + */ +function entity_reference_entity_bundle_delete($entity_type_id, $bundle) { + // Gather a list of all entity reference fields. + $map = \Drupal::entityManager()->getFieldMapByFieldType('entity_reference'); + $ids = []; + foreach ($map as $type => $info) { + foreach ($info as $name => $data) { + foreach ($data['bundles'] as $bundle) { + $ids[] = "$type.$bundle.$name"; + } + } + } + + // Update the 'target_bundles' handler setting if needed. + foreach (FieldConfig::loadMultiple($ids) as $field_config) { + if ($field_config->getSetting('target_type') == $entity_type_id) { + $handler_settings = $field_config->getSetting('handler_settings'); + if (isset($handler_settings['target_bundles'][$bundle])) { + unset($handler_settings['target_bundles'][$bundle]); + $field_config->setSetting('handler_settings', $handler_settings); + $field_config->save(); + + // In case we deleted the only target bundle allowed by the field we + // have to log a warning message because the field will not function + // correctly anymore. + if ($handler_settings['target_bundles'] === []) { + \Drupal::logger('entity_reference')->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [ + '%target_bundle' => $bundle, + '%target_entity_type' => $entity_type_id, + '%field_name' => $field_config->getName(), + '%entity_type' => $field_config->getTargetEntityTypeId(), + '%bundle' => $field_config->getTargetBundle() + ]); } } } } - // Release memory. - unset($map, $ids); } diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php index 17e986f..a7e6177 100644 --- a/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php +++ b/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php @@ -66,7 +66,7 @@ protected function setUp() { /** * Tests that bundle changes are mirrored in field definitions. */ - public function testSyncTargetBundleChanges() { + public function testTargetBundleChanges() { // Attach an entity reference field to $this->nodeType. $name = Unicode::strtolower($this->randomMachineName()); $label = $this->randomString(); diff --git a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php index 25e7393..f965149 100644 --- a/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php +++ b/core/modules/views/src/Plugin/EntityReferenceSelection/ViewsSelection.php @@ -96,6 +96,14 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta } /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + // Don't call the parent validation handler because we don't have any + // 'target_bundles' setting. + } + + /** * Initializes a view. * * @param string|null $match