Issue #2966137 by vaplas, tim.plunkett: Circular entity references cause infinite loop in EntityReferenceItem::generateSampleValue()

merge-requests/1654/head
Alex Pott 2018-05-12 17:41:38 +01:00
parent 75850cddaf
commit 89a7f66bc8
No known key found for this signature in database
GPG Key ID: 31905460D4A69276
2 changed files with 46 additions and 2 deletions

View File

@ -272,6 +272,10 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
// An associative array keyed by the reference type, target type, and
// bundle.
static $recursion_tracker = [];
$manager = \Drupal::service('plugin.manager.entity_reference_selection');
// Instead of calling $manager->getSelectionHandler($field_definition)
@ -300,9 +304,25 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf
// Attempt to create a sample entity, avoiding recursion.
$entity_storage = \Drupal::entityTypeManager()->getStorage($options['target_type']);
if ($options['target_type'] !== $field_definition->getTargetEntityTypeId() && $entity_storage instanceof ContentEntityStorageInterface) {
if ($entity_storage instanceof ContentEntityStorageInterface) {
$bundle = static::getRandomBundle($entity_type, $options['handler_settings']);
$values['entity'] = $entity_storage->createWithSampleValues($bundle);
// Track the generated entity by reference type, target type, and bundle.
$key = $field_definition->getTargetEntityTypeId() . ':' . $options['target_type'] . ':' . $bundle;
// If entity generation was attempted but did not finish, do not continue.
if (isset($recursion_tracker[$key])) {
return [];
}
// Mark this as an attempt at generation.
$recursion_tracker[$key] = TRUE;
// Mark the sample entity as being a preview.
$values['entity'] = $entity_storage->createWithSampleValues($bundle, ['in_preview' => TRUE]);
// Remove the indicator once the entity is successfully generated.
unset($recursion_tracker[$key]);
return $values;
}
}

View File

@ -3,6 +3,7 @@
namespace Drupal\Tests\field\Kernel\EntityReference;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Entity\CommentType;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldItemInterface;
@ -14,6 +15,7 @@ use Drupal\entity_test\Entity\EntityTestStringId;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\TermInterface;
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
@ -89,6 +91,14 @@ class EntityReferenceItemTest extends FieldKernelTestBase {
]);
$this->term->save();
NodeType::create([
'type' => $this->randomMachineName(),
])->save();
CommentType::create([
'id' => $this->randomMachineName(),
'target_entity_type_id' => 'node',
])->save();
$this->entityStringId = EntityTestStringId::create([
'id' => $this->randomMachineName(),
]);
@ -102,6 +112,7 @@ class EntityReferenceItemTest extends FieldKernelTestBase {
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_user', 'Test user entity reference', 'user');
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_comment', 'Test comment entity reference', 'comment');
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_file', 'Test file entity reference', 'file');
$this->createEntityReferenceField('entity_test_string_id', 'entity_test_string_id', 'field_test_entity_test', 'Test content entity reference with string ID', 'entity_test');
}
/**
@ -211,6 +222,19 @@ class EntityReferenceItemTest extends FieldKernelTestBase {
$this->entityValidateAndSave($entity);
}
/**
* Tests the ::generateSampleValue() method when it has a circular reference.
*/
public function testGenerateSampleValueCircularReference() {
// Delete the existing entity.
$this->entityStringId->delete();
$entity_storage = \Drupal::entityTypeManager()->getStorage('entity_test');
$entity = $entity_storage->createWithSampleValues('entity_test');
$this->assertInstanceOf(EntityTestStringId::class, $entity->field_test_entity_test_string_id->entity);
$this->assertInstanceOf(EntityTest::class, $entity->field_test_entity_test_string_id->entity->field_test_entity_test->entity);
}
/**
* Tests referencing content entities with string IDs.
*/