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 cf80690559
)
merge-requests/674/head
parent
a641739d27
commit
fdb10cc79d
|
@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
|
||||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||||
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
|
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
|
||||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||||
|
use Drupal\Core\Field\FieldException;
|
||||||
use Drupal\Core\Field\FieldItemBase;
|
use Drupal\Core\Field\FieldItemBase;
|
||||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||||
use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface;
|
use Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface;
|
||||||
|
@ -67,6 +68,12 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf
|
||||||
$settings = $field_definition->getSettings();
|
$settings = $field_definition->getSettings();
|
||||||
$target_type_info = \Drupal::entityTypeManager()->getDefinition($settings['target_type']);
|
$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';
|
$target_id_data_type = 'string';
|
||||||
if ($target_type_info->entityClassImplements(FieldableEntityInterface::class)) {
|
if ($target_type_info->entityClassImplements(FieldableEntityInterface::class)) {
|
||||||
$id_definition = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($settings['target_type'])[$target_type_info->getKey('id')];
|
$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'] = [
|
$element['target_type'] = [
|
||||||
'#type' => 'select',
|
'#type' => 'select',
|
||||||
'#title' => t('Type of item to reference'),
|
'#title' => t('Type of item to reference'),
|
||||||
'#options' => \Drupal::service('entity_type.repository')->getEntityTypeLabels(TRUE),
|
|
||||||
'#default_value' => $this->getSetting('target_type'),
|
'#default_value' => $this->getSetting('target_type'),
|
||||||
'#required' => TRUE,
|
'#required' => TRUE,
|
||||||
'#disabled' => $has_data,
|
'#disabled' => $has_data,
|
||||||
'#size' => 1,
|
'#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;
|
return $element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,8 +635,9 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render API callback: Processes the field settings form and allows access to
|
* Render API callback: Processes the field settings form.
|
||||||
* the form state.
|
*
|
||||||
|
* Allows access to the form state.
|
||||||
*
|
*
|
||||||
* @see static::fieldSettingsForm()
|
* @see static::fieldSettingsForm()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue