From cb451afa0293ef1838dc4c2c9bde1dcaaf8452e7 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Fri, 12 Jun 2015 00:24:48 +0100
Subject: [PATCH] Issue #2107249 by Jelle_S, tstoeckler, amateescu, Xano,
 jibran, yched: Don't assume that content entities have numeric IDs in
 EntityReferenceItem

---
 .../Field/FieldType/EntityReferenceItem.php   | 14 ++--
 .../EntityReferenceItemTest.php               | 28 ++++++++
 .../tests/src/Unit/EntityViewsDataTest.php    | 72 ++++++++++++++-----
 3 files changed, 92 insertions(+), 22 deletions(-)

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 47874bece5f8..fa0b4a04e0c9 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -72,9 +72,15 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel
     $settings = $field_definition->getSettings();
     $target_type_info = \Drupal::entityManager()->getDefinition($settings['target_type']);
 
+    $target_id_data_type = 'string';
     if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
-      // @todo: Lookup the entity type's ID data type and use it here.
-      // https://www.drupal.org/node/2107249
+      $id_definition = \Drupal::entityManager()->getBaseFieldDefinitions($settings['target_type'])[$target_type_info->getKey('id')];
+      if ($id_definition->getType() === 'integer') {
+        $target_id_data_type = 'integer';
+      }
+    }
+
+    if ($target_id_data_type === 'integer') {
       $target_id_definition = DataDefinition::create('integer')
         ->setLabel(t('@label ID', array($target_type_info->getLabel())))
         ->setSetting('unsigned', TRUE);
@@ -119,8 +125,8 @@ public static function mainPropertyName() {
   public static function schema(FieldStorageDefinitionInterface $field_definition) {
     $target_type = $field_definition->getSetting('target_type');
     $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-
-    if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
+    $properties = static::propertyDefinitions($field_definition)['target_id'];
+    if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface') && $properties->getDataType() === 'integer') {
       $columns = array(
         'target_id' => array(
           'description' => 'The ID of the target entity.',
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
index e75f33aaaf9e..4601c343f17e 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceItemTest.php
@@ -12,6 +12,8 @@
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\entity_test\Entity\EntityTestStringId;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\Tests\FieldUnitTestBase;
@@ -49,12 +51,20 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
    */
   protected $term;
 
+  /**
+   * The test entity with a string ID.
+   *
+   * @var \Drupal\entity_test\Entity\EntityTestStringId
+   */
+  protected $entityStringId;
+
   /**
    * Sets up the test.
    */
   protected function setUp() {
     parent::setUp();
 
+    $this->installEntitySchema('entity_test_string_id');
     $this->installEntitySchema('taxonomy_term');
 
     $this->vocabulary = entity_create('taxonomy_vocabulary', array(
@@ -71,8 +81,14 @@ protected function setUp() {
     ));
     $this->term->save();
 
+    $this->entityStringId = EntityTestStringId::create([
+      'id' => $this->randomMachineName(),
+    ]);
+    $this->entityStringId->save();
+
     // Use the util to create an instance.
     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_term', 'Test content entity reference', 'taxonomy_term');
+    $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_entity_test_string_id', 'Test content entity reference with string ID', 'entity_test_string_id');
     $this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_vocabulary', 'Test config entity reference', 'taxonomy_vocabulary');
   }
 
@@ -152,6 +168,18 @@ public function testContentEntityReferenceItem() {
     $this->entityValidateAndSave($entity);
   }
 
+  /**
+   * Tests referencing content entities with string IDs.
+   */
+  public function testContentEntityReferenceItemWithStringId() {
+    $entity = EntityTest::create();
+    $entity->field_test_entity_test_string_id->target_id = $this->entityStringId->id();
+    $entity->save();
+    $storage = \Drupal::entityManager()->getStorage('entity_test');
+    $storage->resetCache();
+    $this->assertEqual($this->entityStringId->id(), $storage->load($entity->id())->field_test_entity_test_string_id->target_id);
+  }
+
   /**
    * Tests the entity reference field type for referencing config entities.
    */
diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
index fb347941070a..3dedb8252a59 100644
--- a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
+++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
@@ -83,6 +83,12 @@ protected function setUp() {
       ->getMock();
     $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
 
+    $typed_data_manager = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $typed_data_manager->expects($this->any())
+        ->method('createDataDefinition')
+        ->willReturn($this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'));
     $this->baseEntityType = new TestEntityType([
       'base_table' => 'entity_test',
       'id' => 'entity_test',
@@ -110,6 +116,7 @@ protected function setUp() {
     $container = new ContainerBuilder();
     $container->set('plugin.manager.field.field_type', $field_type_manager);
     $container->set('entity.manager', $this->entityManager);
+    $container->set('typed_data_manager', $typed_data_manager);
     \Drupal::setContainer($container);
   }
 
@@ -360,6 +367,9 @@ protected function setupFieldStorageDefinition() {
       ->method('getSetting')
       ->with('target_type')
       ->willReturn('user');
+    $user_id_field_storage_definition->expects($this->any())
+      ->method('getSettings')
+      ->willReturn(['target_type' => 'user']);
     $user_id_field_storage_definition->expects($this->any())
       ->method('getSchema')
       ->willReturn(EntityReferenceItem::schema($user_id_field_storage_definition));
@@ -389,12 +399,19 @@ protected function setupFieldStorageDefinition() {
    */
   public function testBaseTableFields() {
     $base_field_definitions = $this->setupBaseFields(EntityTest::baseFieldDefinitions($this->baseEntityType));
-
-    $this->entityManager->expects($this->once())
+    $user_base_field_definitions = [
+      'uid' => BaseFieldDefinition::create('integer')
+        ->setLabel(t('ID'))
+        ->setDescription(t('The ID of the user entity.'))
+        ->setReadOnly(TRUE)
+        ->setSetting('unsigned', TRUE)
+    ];
+    $this->entityManager->expects($this->any())
       ->method('getBaseFieldDefinitions')
-      ->with('entity_test')
-      ->willReturn($base_field_definitions);
-
+      ->will($this->returnValueMap([
+        ['user', $user_base_field_definitions],
+        ['entity_test', $base_field_definitions],
+      ]));
     // Setup the table mapping.
     $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
     $table_mapping->expects($this->any())
@@ -471,19 +488,21 @@ public function testDataTableFields() {
       ->setSettings(array('target_type' => 'entity_test_bundle'))
       ->setTranslatable(TRUE);
     $base_field_definitions = $this->setupBaseFields($base_field_definitions);
+    $user_base_field_definitions = [
+      'uid' => BaseFieldDefinition::create('integer')
+        ->setLabel(t('ID'))
+        ->setDescription(t('The ID of the user entity.'))
+        ->setReadOnly(TRUE)
+        ->setSetting('unsigned', TRUE)
+    ];
     $entity_test_type = new ConfigEntityType(['id' => 'entity_test_bundle']);
-    $user_entity_type = static::userEntityInfo();
-    $this->entityManager->expects($this->any())
-      ->method('getDefinition')
-      ->willReturnMap([
-        ['entity_test_bundle', TRUE, $entity_test_type],
-        ['user', TRUE, $user_entity_type],
-      ]);
 
-    $this->entityManager->expects($this->once())
+    $this->entityManager->expects($this->any())
       ->method('getBaseFieldDefinitions')
-      ->with('entity_test_mul')
-      ->willReturn($base_field_definitions);
+      ->will($this->returnValueMap([
+        ['user', $user_base_field_definitions],
+        ['entity_test_mul', $base_field_definitions],
+      ]));
 
     $this->viewsData->setEntityType($entity_type);
 
@@ -517,6 +536,14 @@ public function testDataTableFields() {
 
     $this->setupFieldStorageDefinition();
 
+    $user_entity_type = static::userEntityInfo();
+    $this->entityManager->expects($this->any())
+      ->method('getDefinition')
+      ->will($this->returnValueMap([
+        ['user', TRUE, $user_entity_type],
+        ['entity_test_bundle', TRUE, $entity_test_type],
+      ]));
+
     $data = $this->viewsData->getViewsData();
 
     // Check the base fields.
@@ -575,10 +602,19 @@ public function testRevisionTableFields() {
       ->set('revision_data_table', 'entity_test_mulrev_property_revision')
       ->set('id', 'entity_test_mulrev');
     $base_field_definitions = $this->setupBaseFields(EntityTestMulRev::baseFieldDefinitions($this->baseEntityType));
-    $this->entityManager->expects($this->once())
+    $user_base_field_definitions = [
+      'uid' => BaseFieldDefinition::create('integer')
+        ->setLabel(t('ID'))
+        ->setDescription(t('The ID of the user entity.'))
+        ->setReadOnly(TRUE)
+        ->setSetting('unsigned', TRUE)
+    ];
+    $this->entityManager->expects($this->any())
       ->method('getBaseFieldDefinitions')
-      ->with('entity_test_mulrev')
-      ->willReturn($base_field_definitions);
+      ->will($this->returnValueMap([
+        ['user', $user_base_field_definitions],
+        ['entity_test_mulrev', $base_field_definitions],
+      ]));
 
     $this->viewsData->setEntityType($entity_type);
 
-- 
GitLab