From fdb10cc79d4824b44864f012ecd864b441e06f30 Mon Sep 17 00:00:00 2001 From: Lee Rowlands Date: Tue, 11 May 2021 08:03:57 +1000 Subject: [PATCH] Issue #2608750 by phenaproxima, shriaas2898, KapilV, mohit_aghera, RenatoG, akhoury, guilhermevp, praveenmoses61, sulfikar_s, Abhijith S, larowlan, pameeela, Sid_omp: Exception when creating an entity reference field targeting an entity type without an ID (cherry picked from commit cf80690559ff1b09703f68fc379c584d0d94dc42) --- .../Field/FieldType/EntityReferenceItem.php | 24 +++++++-- .../src/Kernel/ContactReferenceFieldTest.php | 50 ++++++++++++++++++ .../Functional/Entity/EntityFieldUITest.php | 48 +++++++++++++++++ .../EntityReferenceFieldCreationTest.php | 51 +++++++++++++++++++ 4 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 core/modules/contact/tests/src/Kernel/ContactReferenceFieldTest.php create mode 100644 core/modules/system/tests/src/Functional/Entity/EntityFieldUITest.php create mode 100644 core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php 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 01761e14c5b..dbe2762d940 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\TypedData\EntityDataDefinition; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldException; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface; @@ -67,6 +68,12 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf $settings = $field_definition->getSettings(); $target_type_info = \Drupal::entityTypeManager()->getDefinition($settings['target_type']); + // If the target entity type doesn't have an ID key, we cannot determine + // the target_id data type. + if (!$target_type_info->hasKey('id')) { + throw new FieldException('Entity type "' . $target_type_info->id() . '" has no ID key and cannot be targeted by entity reference field "' . $field_definition->getName() . '"'); + } + $target_id_data_type = 'string'; if ($target_type_info->entityClassImplements(FieldableEntityInterface::class)) { $id_definition = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($settings['target_type'])[$target_type_info->getKey('id')]; @@ -356,13 +363,23 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf $element['target_type'] = [ '#type' => 'select', '#title' => t('Type of item to reference'), - '#options' => \Drupal::service('entity_type.repository')->getEntityTypeLabels(TRUE), '#default_value' => $this->getSetting('target_type'), '#required' => TRUE, '#disabled' => $has_data, '#size' => 1, ]; + // Only allow the field to target entity types that have an ID key. This + // is enforced in ::propertyDefinitions(). + $entity_type_manager = \Drupal::entityTypeManager(); + $filter = function (string $entity_type_id) use ($entity_type_manager): bool { + return $entity_type_manager->getDefinition($entity_type_id) + ->hasKey('id'); + }; + $options = \Drupal::service('entity_type.repository')->getEntityTypeLabels(TRUE); + foreach ($options as $group_name => $group) { + $element['target_type']['#options'][$group_name] = array_filter($group, $filter, ARRAY_FILTER_USE_KEY); + } return $element; } @@ -618,8 +635,9 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf } /** - * Render API callback: Processes the field settings form and allows access to - * the form state. + * Render API callback: Processes the field settings form. + * + * Allows access to the form state. * * @see static::fieldSettingsForm() */ diff --git a/core/modules/contact/tests/src/Kernel/ContactReferenceFieldTest.php b/core/modules/contact/tests/src/Kernel/ContactReferenceFieldTest.php new file mode 100644 index 00000000000..5278e64e22b --- /dev/null +++ b/core/modules/contact/tests/src/Kernel/ContactReferenceFieldTest.php @@ -0,0 +1,50 @@ +installConfig('node'); + } + + /** + * Tests creating an entity reference field targeting contact messages. + */ + public function testCreateContactMessageReferenceField(): void { + $node_type = $this->createContentType()->id(); + + $this->expectException('\Drupal\Core\Field\FieldException'); + $this->expectExceptionMessage('Entity type "contact_message" has no ID key and cannot be targeted by entity reference field "field_messages"'); + $this->createEntityReferenceField('node', $node_type, 'field_messages', 'Messages', 'contact_message'); + } + +} diff --git a/core/modules/system/tests/src/Functional/Entity/EntityFieldUITest.php b/core/modules/system/tests/src/Functional/Entity/EntityFieldUITest.php new file mode 100644 index 00000000000..3156118ea31 --- /dev/null +++ b/core/modules/system/tests/src/Functional/Entity/EntityFieldUITest.php @@ -0,0 +1,48 @@ +drupalLogin($this->rootUser); + $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + } + + /** + * Tests the add page for an entity type using bundle entities. + */ + public function testAddEntityReferenceField() { + $this->drupalGet('admin/structure/types/manage/article/fields/add-field'); + $edit = [ + 'new_storage_type' => 'entity_reference', + 'label' => 'Test Field', + 'field_name' => 'test_reference_field', + ]; + $this->submitForm($edit, 'Save and continue'); + $this->assertSession()->optionNotExists('settings[target_type]', 'entity_test_no_id'); + } + +} diff --git a/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php new file mode 100644 index 00000000000..9062c5cf81e --- /dev/null +++ b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php @@ -0,0 +1,51 @@ +drupalLogin($this->rootUser); + $node_type = $this->drupalCreateContentType()->id(); + + // Entity types without an ID key should not be presented as options when + // creating an entity reference field in the UI. + $this->drupalGet("/admin/structure/types/manage/$node_type/fields/add-field"); + $edit = [ + 'new_storage_type' => 'entity_reference', + 'label' => 'Test Field', + 'field_name' => 'test_reference_field', + ]; + $this->submitForm($edit, 'Save and continue'); + $this->assertSession()->optionNotExists('settings[target_type]', 'entity_test_no_id'); + + // Trying to do it programmatically should raise an exception. + $this->expectException('\Drupal\Core\Field\FieldException'); + $this->expectExceptionMessage('Entity type "entity_test_no_id" has no ID key and cannot be targeted by entity reference field "test_reference_field"'); + $this->createEntityReferenceField('node', $node_type, 'test_reference_field', 'Test Field', 'entity_test_no_id'); + } + +}