function EntityReferenceFormatterTest::testEntityFormatterRecursiveRendering

Same name and namespace in other branches
  1. 10 core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php \Drupal\Tests\field\Kernel\EntityReference\EntityReferenceFormatterTest::testEntityFormatterRecursiveRendering()
  2. 9 core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php \Drupal\Tests\field\Kernel\EntityReference\EntityReferenceFormatterTest::testEntityFormatterRecursiveRendering()
  3. 8.9.x core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php \Drupal\Tests\field\Kernel\EntityReference\EntityReferenceFormatterTest::testEntityFormatterRecursiveRendering()

Tests the recursive rendering protection of the entity formatter.

File

core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php, line 278

Class

EntityReferenceFormatterTest
Tests the formatters functionality.

Namespace

Drupal\Tests\field\Kernel\EntityReference

Code

public function testEntityFormatterRecursiveRendering() : void {
  // Intercept our specific warning and suppress it.
  set_error_handler(function (int $errno, string $errstr) : bool {
    return $errno === E_USER_WARNING && str_starts_with($errstr, 'Recursive rendering attempt aborted');
  });
  /** @var \Drupal\Core\Render\RendererInterface $renderer */
  $renderer = $this->container
    ->get('renderer');
  $view_builder = $this->entityTypeManager
    ->getViewBuilder($this->entityType);
  $this->createNonCacheableViewMode();
  // Set the default view mode to use the 'entity_reference_entity_view'
  // formatter.
  \Drupal::service('entity_display.repository')->getViewDisplay($this->entityType, $this->bundle)
    ->setComponent($this->fieldName, [
    'type' => 'entity_reference_entity_view',
  ])
    ->save();
  $storage = \Drupal::entityTypeManager()->getStorage($this->entityType);
  $referencing_entity_1 = $storage->create([
    'name' => $this->randomMachineName(),
  ]);
  $referencing_entity_1->save();
  // Create a self-reference.
  $referencing_entity_1->{$this->fieldName}->entity = $referencing_entity_1;
  $referencing_entity_1->save();
  $referencing_entity_1_label = $referencing_entity_1->label();
  // Using a different view mode is not recursion.
  $build = $view_builder->view($referencing_entity_1, 'teaser');
  $output = (string) $renderer->renderRoot($build);
  // 2 occurrences of the entity title per entity.
  $expected_occurrences = 4;
  $actual_occurrences = substr_count($output, $referencing_entity_1_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  // Self-references should not be rendered.
  // entity_1 -> entity_1
  $build = $view_builder->view($referencing_entity_1, $this->nonCacheableViewMode);
  $output = (string) $renderer->renderRoot($build);
  $expected_occurrences = 2;
  $actual_occurrences = substr_count($output, $referencing_entity_1_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  $build = $view_builder->view($referencing_entity_1, $this->nonCacheableViewMode);
  // Repetition is not wrongly detected as recursion.
  // entity_1 -> entity_1
  $output = (string) $renderer->renderRoot($build);
  $actual_occurrences = substr_count($output, $referencing_entity_1_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  // Referencing from another entity works fine.
  // entity_2 -> entity_1
  $referencing_entity_2 = $storage->create([
    'name' => $this->randomMachineName(),
  ]);
  $referencing_entity_2->save();
  $referencing_entity_2_label = $referencing_entity_2->label();
  $referencing_entity_2->{$this->fieldName}->entity = $referencing_entity_1;
  $referencing_entity_2->save();
  $build = $view_builder->view($referencing_entity_2, $this->nonCacheableViewMode);
  $output = (string) $renderer->renderRoot($build);
  $actual_occurrences = substr_count($output, $referencing_entity_1_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  // Referencing from multiple is fine.
  // entity_1 -> entity_1
  // entity_2 -> entity_1
  $build = $view_builder->viewMultiple([
    $referencing_entity_1,
    $referencing_entity_2,
  ], $this->nonCacheableViewMode);
  $output = (string) $renderer->renderRoot($build);
  // entity_1 should be seen once as a parent and once as a child of entity_2.
  $expected_occurrences = 4;
  $actual_occurrences = substr_count($output, $referencing_entity_1_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  // entity_2 is seen only once, as a parent.
  $expected_occurrences = 2;
  $actual_occurrences = substr_count($output, $referencing_entity_2_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  // Indirect recursion is not ok.
  // entity_2 -> entity_1 -> entity_2
  $referencing_entity_1->{$this->fieldName}->entity = $referencing_entity_2;
  $referencing_entity_1->save();
  $build = $view_builder->view($referencing_entity_2, $this->nonCacheableViewMode);
  $output = (string) $renderer->renderRoot($build);
  // Each entity should be seen once.
  $expected_occurrences = 2;
  $actual_occurrences = substr_count($output, $referencing_entity_1_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  $expected_occurrences = 2;
  $actual_occurrences = substr_count($output, $referencing_entity_2_label);
  $this->assertEquals($expected_occurrences, $actual_occurrences);
  restore_error_handler();
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.