Unverified Commit 87bdf2c9 authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #2608750 by phenaproxima, shriaas2898, KapilV, mohit_aghera, RenatoG,...

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 cf806905)
parent 9836e65b
Loading
Loading
Loading
Loading
+21 −3
Original line number Diff line number Diff line
@@ -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()
   */
+50 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\contact\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;

/**
 * Tests entity reference fields that target contact messages.
 *
 * @group contact
 */
class ContactReferenceFieldTest extends KernelTestBase {

  use ContentTypeCreationTrait;
  use EntityReferenceTestTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'contact',
    'field',
    'node',
    'system',
    'text',
    'user',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->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');
  }

}
+48 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\system\Functional\Entity;

use Drupal\Tests\BrowserTestBase;

/**
 * Tests that while adding fields to entity types entity which doesn't have id
 * shouldn't appear.
 *
 * @group entity
 */
class EntityFieldUITest extends BrowserTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['entity_test', 'node', 'field_ui', 'field'];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * {@inheritdoc}
   */
  public function setUp(): void {
    parent::setUp();
    $this->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');
  }

}
+51 −0
Original line number Diff line number Diff line
<?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');
  }

}