Unverified Commit ccd4f455 authored by jibran's avatar jibran Committed by jibran

Issue #2878852 by jibran: Fix incorrect DER field config dependency calculation

parent 638a652b
......@@ -7,8 +7,59 @@
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'.
*
......
......@@ -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();
}
}
}
}
}
}
......@@ -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;
}
......
<?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']));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment