diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 3ab60f5..40f0efe 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -30,6 +30,7 @@ use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TypedDataManager; +use Drupal\field\Entity\FieldConfig; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -168,6 +169,14 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa protected $fieldMapByFieldType = array(); /** + * A list of entity reference fields targeting to a specific entity type and + * bundle. + * + * @var array + */ + protected $fieldsReferencing = array(); + + /** * Contains cached mappings of class names to entity types. * * @var array @@ -678,6 +687,42 @@ public function getFieldMapByFieldType($field_type) { } /** + * {@inheritdoc} + */ + public function getFieldsReferencing($target_type, $target_bundle) { + if (!isset($this->fieldsReferencing[$target_type][$target_bundle])) { + $map = $this->getFieldMapByFieldType('entity_reference'); + $ids = []; + foreach ($map as $type => $info) { + foreach ($info as $name => $data) { + foreach ($data['bundles'] as $bundle) { + $ids[] = "$type.$bundle.$name"; + } + } + } + + $this->fieldsReferencing[$target_type][$target_bundle] = []; + /** @var \Drupal\field\Entity\FieldConfig $config */ + foreach (FieldConfig::loadMultiple($ids) as $config) { + $type = $config->getSetting('target_type'); + $handler = $config->getSetting('handler_settings'); + if ($type == $target_type) { + $bundles = !empty($handler['target_bundles']) ? array_keys($handler['target_bundles']) : []; + foreach ($bundles as $bundle) { + if ($bundle == $target_bundle) { + $this->fieldsReferencing[$target_type][$target_bundle][] = $config->id(); + break; + } + } + } + } + unset($map, $ids); + } + + return $this->fieldsReferencing[$target_type][$target_bundle]; + } + + /** * Builds field storage definitions for an entity type. * * @param string $entity_type_id diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php index b3bda8b..5626fa6 100644 --- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php @@ -143,6 +143,20 @@ public function getFieldMap(); public function getFieldMapByFieldType($field_type); /** + * Returns a list of entity reference field configurations that are targeting + * a specific bundle. + * + * @param string $target_type + * The entity type of target + * @param string $target_bundle + * The bundle of target. + * + * @return string[] + * A list of field IDs. + */ + public function getFieldsReferencing($target_type, $target_bundle); + + /** * Creates a new access control handler instance. * * @param string $entity_type diff --git a/core/modules/entity_reference/entity_reference.module b/core/modules/entity_reference/entity_reference.module index e6d764b..369f7b5 100644 --- a/core/modules/entity_reference/entity_reference.module +++ b/core/modules/entity_reference/entity_reference.module @@ -202,3 +202,43 @@ function entity_reference_query_entity_reference_alter(AlterableInterface $query $handler = $query->getMetadata('entity_reference_selection_handler'); $handler->entityQueryAlter($query); } + +/** + * Implements hook_entity_bundle_rename(). + */ +function entity_reference_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) { + entity_reference_sync_fields($entity_type, $bundle_old, $bundle_new); +} + +/** + * Implements hook_entity_bundle_delete(). + */ +function entity_reference_entity_bundle_delete($entity_type, $bundle) { + entity_reference_sync_fields($entity_type, $bundle); +} + +/** + * Sync bundle settings of entity reference fields targeting a bundle that is + * about to change. + * + * @param string $entity_type + * The entity type of the bundle that is about to change. + * @param string $bundle + * The bundle that is about to change. + * @param string|null $bundle_new + * (optional) If provided the bundle has been renamed to this new name. If + * omitted, the bundle has been deleted. + */ +function entity_reference_sync_fields($entity_type, $bundle, $bundle_new = NULL) { + $manager = \Drupal::entityManager(); + $ids = $manager->getFieldsReferencing($entity_type, $bundle); + + /** @var \Drupal\field\Entity\FieldConfig $field_config */ + foreach (FieldConfig::loadMultiple($ids) as $field_config) { + if ($bundle_new) { + $field_config->settings['handler_settings']['target_bundles'][$bundle_new] = $bundle_new; + } + unset($field_config->settings['handler_settings']['target_bundles'][$bundle]); + $field_config->save(); + } +} diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php new file mode 100644 index 0000000..c8d6f2a --- /dev/null +++ b/core/modules/entity_reference/src/Tests/EntityReferenceSettingsTest.php @@ -0,0 +1,97 @@ +installEntitySchema('node'); + $this->installEntitySchema('taxonomy_term'); + + $this->nodeType = NodeType::create([ + 'type' => Unicode::strtolower($this->randomMachineName()), + 'name' => $this->randomString(), + ]); + $this->nodeType->save(); + + $this->vocabulary = Vocabulary::create([ + 'vid' => Unicode::strtolower($this->randomMachineName()), + 'name' => $this->randomString(), + ]); + $this->vocabulary->save(); + } + + /** + * Tests that bundle changes are mirrored in field definitions. + */ + public function testSyncTargetBundleChanges() { + // Attach an entity reference field to $this->nodeType. + $name = Unicode::strtolower($this->randomMachineName()); + $label = $this->randomString(); + $vid = $this->vocabulary->id(); + $handler_settings = ['target_bundles' => [$vid => $vid]]; + $this->createEntityReferenceField('node', $this->nodeType->id(), $name, $label, 'taxonomy_term', 'default', $handler_settings); + + // Change the vocabulary vid. + $new_vid = Unicode::strtolower($this->randomMachineName()); + $this->vocabulary->set('vid', $new_vid); + $this->vocabulary->save(); + + // Reload the field config and test the target vocabulary. + $field_config = FieldConfig::loadByName('node', $this->nodeType->id(), $name); + $handler_settings = $field_config->getSetting('handler_settings'); + $vid = key($handler_settings['target_bundles']); + $this->assertIdentical($vid, $new_vid); + + // Delete the vocabulary. + $this->vocabulary->delete(); + // Reload and test again. + $field_config = FieldConfig::loadByName('node', $this->nodeType->id(), $name); + $handler_settings = $field_config->getSetting('handler_settings'); + $this->assertTrue(empty($handler_settings['target_bundles'])); + } + +}