summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjibran2017-05-21 20:13:14 (GMT)
committerJibran Ijaz2017-05-21 20:13:14 (GMT)
commitccd4f455afcae2802b4412419ac1f3460373d7e1 (patch)
tree1baa42ed3b03d4ed57eb4b8e5e30b6f509a135a7
parent638a652ba3bb04f1e9a8812b47f8867c196187a2 (diff)
Issue #2878852 by jibran: Fix incorrect DER field config dependency calculation8.x-1.3
-rw-r--r--dynamic_entity_reference.module51
-rw-r--r--dynamic_entity_reference.post_update.php24
-rw-r--r--src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php73
-rw-r--r--tests/src/Kernel/DynamicEntityReferenceSettingsTest.php188
4 files changed, 333 insertions, 3 deletions
diff --git a/dynamic_entity_reference.module b/dynamic_entity_reference.module
index 6f987b9..cdb7251 100644
--- a/dynamic_entity_reference.module
+++ b/dynamic_entity_reference.module
@@ -7,9 +7,60 @@
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceItem;
+use Drupal\field\Entity\FieldConfig;
use Drupal\field\FieldConfigInterface;
/**
+ * Implements hook_entity_bundle_delete().
+ *
+ * We are duplicating the work done by
+ * \Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceItem::onDependencyRemoval()
+ * because we need to take into account bundles that are not provided by a
+ * config entity type so they are not part of the config dependencies.
+ */
+function dynamic_entity_reference_entity_bundle_delete($entity_type_id, $bundle) {
+
+ // Gather a list of all entity reference fields.
+ $map = Drupal::service('entity_field.manager')->getFieldMapByFieldType('dynamic_entity_reference');
+ $ids = [];
+ foreach ($map as $type => $info) {
+ foreach ($info as $name => $data) {
+ foreach ($data['bundles'] as $bundle_name) {
+ $ids[] = "$type.$bundle_name.$name";
+ }
+ }
+ }
+
+ // Update the 'target_bundles' handler setting if needed.
+ foreach (FieldConfig::loadMultiple($ids) as $field_config) {
+ $settings = $field_config->getSettings();
+ $target_types = DynamicEntityReferenceItem::getTargetTypes($settings);
+ if (in_array($entity_type_id, $target_types, TRUE)) {
+ $handler_settings = $settings[$entity_type_id]['handler_settings'];
+ if (isset($handler_settings['target_bundles'][$bundle])) {
+ unset($handler_settings['target_bundles'][$bundle]);
+ $settings[$entity_type_id]['handler_settings'] = $handler_settings;
+ $field_config->setSettings($settings);
+ $field_config->save();
+
+ // In case we deleted the only target bundle allowed by the field we
+ // have to log a critical message because the field will not function
+ // correctly anymore.
+ if ($handler_settings['target_bundles'] === []) {
+ \Drupal::logger('dynamic_entity_reference')->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name dynamic 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(),
+ ]);
+ }
+ }
+ }
+ }
+}
+
+/**
* Implements hook_ENTITY_TYPE_presave() for 'field_config'.
*
* @todo Clean this up once field_field_config_save is more accommodating.
diff --git a/dynamic_entity_reference.post_update.php b/dynamic_entity_reference.post_update.php
index 9af007b..d8960e7 100644
--- a/dynamic_entity_reference.post_update.php
+++ b/dynamic_entity_reference.post_update.php
@@ -5,6 +5,7 @@
* Post update functions for dynamic entity reference.
*/
+use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
@@ -12,7 +13,7 @@ use Drupal\field\Entity\FieldStorageConfig;
*/
function dynamic_entity_reference_post_update_field_storage_dependencies() {
/** @var \Drupal\Core\Entity\EntityFieldManager $entity_field_manager */
- $entity_field_manager = Drupal::service('entity_field.manager');
+ $entity_field_manager = \Drupal::service('entity_field.manager');
$map = $entity_field_manager->getFieldMapByFieldType('dynamic_entity_reference');
foreach ($map as $entity_type_id => $info) {
foreach ($info as $name => $data) {
@@ -23,3 +24,24 @@ function dynamic_entity_reference_post_update_field_storage_dependencies() {
}
}
}
+
+/**
+ * Add config dependencies to reference-able entity types bundles in DER field.
+ */
+function dynamic_entity_reference_post_update_field_config_dependencies() {
+ /** @var \Drupal\Core\Entity\EntityFieldManager $entity_field_manager */
+ $entity_field_manager = \Drupal::service('entity_field.manager');
+ $map = $entity_field_manager->getFieldMapByFieldType('dynamic_entity_reference');
+ foreach ($map as $entity_type_id => $info) {
+ foreach ($info as $name => $data) {
+ if ($field_storage = FieldStorageConfig::loadByName($entity_type_id, $name)) {
+ foreach ($field_storage->getBundles() as $bundle) {
+ if ($field = FieldConfig::loadByName($field_storage->getTargetEntityTypeId(), $bundle, $field_storage->getName())) {
+ // Re-saving the config object will fix the config dependencies.
+ $field->save();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php b/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php
index 89646a4..5b92daf 100644
--- a/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php
+++ b/src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php
@@ -470,11 +470,11 @@ class DynamicEntityReferenceItem extends EntityReferenceItem {
*/
public static function calculateDependencies(FieldDefinitionInterface $field_definition) {
$dependencies = FieldItemBase::calculateDependencies($field_definition);
- $manager = \Drupal::service('entity.repository');
+ $entity_repository = \Drupal::service('entity.repository');
if ($default_value = $field_definition->getDefaultValueLiteral()) {
foreach ($default_value as $value) {
if (is_array($value) && isset($value['target_uuid']) && isset($value['target_type'])) {
- $entity = $manager->loadEntityByUuid($value['target_type'], $value['target_uuid']);
+ $entity = $entity_repository->loadEntityByUuid($value['target_type'], $value['target_uuid']);
// If the entity does not exist do not create the dependency.
// @see \Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceFieldItemList::processDefaultValue()
if ($entity) {
@@ -483,6 +483,24 @@ class DynamicEntityReferenceItem extends EntityReferenceItem {
}
}
}
+ // Depend on target bundle configurations. Dependencies for 'target_bundles'
+ // also covers the 'auto_create_bundle' setting, if any, because its value
+ // is included in the 'target_bundles' list.
+ $entity_type_manager = \Drupal::entityTypeManager();
+ $settings = $field_definition->getSettings();
+ foreach (static::getTargetTypes($settings) as $target_type) {
+ $handler = $settings[$target_type]['handler_settings'];
+ if (!empty($handler['target_bundles'])) {
+ $target_entity_type = $entity_type_manager->getDefinition($target_type);
+ if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) {
+ if ($storage = $entity_type_manager->getStorage($bundle_entity_type_id)) {
+ foreach ($storage->loadMultiple($handler['target_bundles']) as $bundle) {
+ $dependencies[$bundle->getConfigDependencyKey()][] = $bundle->getConfigDependencyName();
+ }
+ }
+ }
+ }
+ }
return $dependencies;
}
@@ -506,6 +524,57 @@ class DynamicEntityReferenceItem extends EntityReferenceItem {
$field_definition->setDefaultValue($default_value);
}
}
+
+ $entity_type_manager = \Drupal::entityTypeManager();
+ // Update the 'target_bundles' handler setting if a bundle config dependency
+ // has been removed.
+ $settings = $field_definition->getSettings();
+ foreach (static::getTargetTypes($settings) as $target_type) {
+ $bundles_changed = FALSE;
+ $handler_settings = $settings[$target_type]['handler_settings'];
+ if (!empty($handler_settings['target_bundles'])) {
+ $target_entity_type = $entity_type_manager->getDefinition($target_type);
+ if ($bundle_entity_type_id = $target_entity_type->getBundleEntityType()) {
+ if ($storage = $entity_type_manager->getStorage($bundle_entity_type_id)) {
+ foreach ($storage->loadMultiple($handler_settings['target_bundles']) as $bundle) {
+ if (isset($dependencies[$bundle->getConfigDependencyKey()][$bundle->getConfigDependencyName()])) {
+ unset($handler_settings['target_bundles'][$bundle->id()]);
+
+ // If this bundle is also used in the 'auto_create_bundle'
+ // setting, disable the auto-creation feature completely.
+ $auto_create_bundle = !empty($handler_settings['auto_create_bundle']) ? $handler_settings['auto_create_bundle'] : FALSE;
+ if ($auto_create_bundle && $auto_create_bundle == $bundle->id()) {
+ $handler_settings['auto_create'] = NULL;
+ $handler_settings['auto_create_bundle'] = NULL;
+ }
+
+ $bundles_changed = TRUE;
+
+ // In case we deleted the only target bundle allowed by the
+ // field we have to log a critical message because the field
+ // will not function correctly anymore.
+ if ($handler_settings['target_bundles'] === []) {
+ \Drupal::logger('dynamic_entity_reference')
+ ->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name dynamic 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->label(),
+ '%target_entity_type' => $bundle->getEntityType()
+ ->getBundleOf(),
+ '%field_name' => $field_definition->getName(),
+ '%entity_type' => $field_definition->getTargetEntityTypeId(),
+ '%bundle' => $field_definition->getTargetBundle(),
+ ]);
+ }
+ }
+ }
+ }
+ }
+ }
+ if ($bundles_changed) {
+ $settings[$target_type]['handler_settings'] = $handler_settings;
+ $field_definition->setSettings($settings);
+ }
+ $changed |= $bundles_changed;
+ }
return $changed;
}
diff --git a/tests/src/Kernel/DynamicEntityReferenceSettingsTest.php b/tests/src/Kernel/DynamicEntityReferenceSettingsTest.php
new file mode 100644
index 0000000..83c9c87
--- /dev/null
+++ b/tests/src/Kernel/DynamicEntityReferenceSettingsTest.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace Drupal\Tests\dynamic_entity_reference\Kernel;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\node\Entity\NodeType;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\taxonomy\Entity\Vocabulary;
+
+/**
+ * Tests dynamic entity reference field settings.
+ *
+ * @group dynamic_entity_reference
+ */
+class DynamicEntityReferenceSettingsTest extends KernelTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = [
+ 'node',
+ 'taxonomy',
+ 'field',
+ 'user',
+ 'text',
+ 'dynamic_entity_reference',
+ 'entity_test',
+ 'system',
+ ];
+
+ /**
+ * Testing node type.
+ *
+ * @var \Drupal\node\Entity\NodeType
+ */
+ protected $nodeType;
+
+ /**
+ * Testing vocabulary.
+ *
+ * @var \Drupal\taxonomy\Entity\Vocabulary
+ */
+ protected $vocabulary;
+
+ /**
+ * An entity bundle that is not stored as a configuration entity.
+ *
+ * @var string
+ */
+ protected $customBundle;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setup();
+
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('taxonomy_term');
+ $this->installEntitySchema('entity_test');
+
+ $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();
+
+ // Create a custom bundle.
+ $this->customBundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
+ entity_test_create_bundle($this->customBundle, NULL, 'entity_test');
+ }
+
+ /**
+ * Tests that config bundle deletions are mirrored in field config settings.
+ */
+ public function testConfigTargetBundleDeletion() {
+ // 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]];
+
+ // Create a field.
+ FieldStorageConfig::create([
+ 'field_name' => $name,
+ 'type' => 'dynamic_entity_reference',
+ 'entity_type' => 'node',
+ 'cardinality' => 1,
+ 'settings' => [
+ 'exclude_entity_types' => FALSE,
+ 'entity_type_ids' => [
+ 'taxonomy_term',
+ ],
+ ],
+ ])->save();
+
+ FieldConfig::create([
+ 'field_name' => $name,
+ 'entity_type' => 'node',
+ 'bundle' => $this->nodeType->id(),
+ 'label' => $label,
+ 'settings' => [
+ 'taxonomy_term' => [
+ 'handler' => 'default:taxonomy_term',
+ 'handler_settings' => $handler_settings,
+ ],
+ ],
+ ])->save();
+
+ // Check that the 'target_bundle' setting contains the vocabulary.
+ $field_config = FieldConfig::loadByName('node', $this->nodeType->id(), $name);
+ $settings = $field_config->getSettings();
+ $actual_handler_settings = $settings['taxonomy_term']['handler_settings'];
+ $this->assertEquals($handler_settings, $actual_handler_settings);
+
+ // Delete the vocabulary.
+ $this->vocabulary->delete();
+
+ // Check that the deleted vocabulary is no longer present in the
+ // 'target_bundles' field setting.
+ $field_config = FieldConfig::loadByName('node', $this->nodeType->id(), $name);
+ $settings = $field_config->getSettings();
+ $handler_settings = $settings['taxonomy_term']['handler_settings'];
+ $this->assertTrue(empty($handler_settings['target_bundles']));
+ }
+
+ /**
+ * Tests that deletions of custom bundles are mirrored in field settings.
+ */
+ public function testCustomTargetBundleDeletion() {
+ // Attach an entity reference field to $this->nodeType.
+ $name = Unicode::strtolower($this->randomMachineName());
+ $label = $this->randomString();
+ $handler_settings = ['target_bundles' => [$this->customBundle => $this->customBundle]];
+
+ // Create a field.
+ FieldStorageConfig::create([
+ 'field_name' => $name,
+ 'type' => 'dynamic_entity_reference',
+ 'entity_type' => 'node',
+ 'cardinality' => 1,
+ 'settings' => [
+ 'exclude_entity_types' => FALSE,
+ 'entity_type_ids' => [
+ 'entity_test',
+ ],
+ ],
+ ])->save();
+
+ FieldConfig::create([
+ 'field_name' => $name,
+ 'entity_type' => 'node',
+ 'bundle' => $this->nodeType->id(),
+ 'label' => $label,
+ 'settings' => [
+ 'entity_test' => [
+ 'handler' => 'default:entity_test',
+ 'handler_settings' => $handler_settings,
+ ],
+ ],
+ ])->save();
+
+ // Check that the 'target_bundle' setting contains the custom bundle.
+ $field_config = FieldConfig::loadByName('node', $this->nodeType->id(), $name);
+ $settings = $field_config->getSettings();
+ $actual_handler_settings = $settings['entity_test']['handler_settings'];
+ $this->assertEquals($handler_settings, $actual_handler_settings);
+
+ // Delete the custom bundle.
+ entity_test_delete_bundle($this->customBundle, 'entity_test');
+
+ // Check that the deleted bundle is no longer present in the
+ // 'target_bundles' field setting.
+ $field_config = FieldConfig::loadByName('node', $this->nodeType->id(), $name);
+ $settings = $field_config->getSettings();
+ $handler_settings = $settings['entity_test']['handler_settings'];
+ $this->assertTrue(empty($handler_settings['target_bundles']));
+ }
+
+}