From 3eebb1b1fb88b8d20eae5263ec8bec767b4acf9c Mon Sep 17 00:00:00 2001 From: Lee Rowlands <lee.rowlands@previousnext.com.au> Date: Tue, 11 May 2021 09:05:35 +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 --- .../Field/FieldType/EntityReferenceItem.php | 24 +++++++-- .../EntityReferenceFieldCreationTest.php | 51 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) 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 01761e14c5b3..dbe2762d940c 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\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 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel $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 @@ public function storageSettingsForm(array &$form, FormStateInterface $form_state $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 @@ public function getSettableOptions(AccountInterface $account = NULL) { } /** - * 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/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php new file mode 100644 index 000000000000..9062c5cf81e8 --- /dev/null +++ b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php @@ -0,0 +1,51 @@ +<?php + +namespace Drupal\Tests\system\Functional\Entity; + +use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\field\Traits\EntityReferenceTestTrait; + +/** + * Tests creating entity reference fields in the UI. + * + * @group entity + */ +class EntityReferenceFieldCreationTest extends BrowserTestBase { + + use EntityReferenceTestTrait; + + /** + * {@inheritdoc} + */ + protected static $modules = ['entity_test', 'node', 'field_ui']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Tests that entity reference fields cannot target entity types without IDs. + */ + public function testAddReferenceFieldTargetingEntityTypeWithoutId() { + $this->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'); + } + +} -- GitLab