e1537bc809c6c3e93f26128a6d757ec119908353
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Entity / EntityViewBuilderTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Entity;
4
5 use Drupal\Core\Entity\EntityViewBuilder;
6 use Drupal\Core\Language\LanguageInterface;
7 use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
8 use Drupal\Core\Cache\Cache;
9 use Drupal\user\Entity\Role;
10 use Drupal\user\RoleInterface;
11
12 /**
13  * Tests the entity view builder.
14  *
15  * @group Entity
16  */
17 class EntityViewBuilderTest extends EntityKernelTestBase {
18
19   use EntityReferenceTestTrait;
20
21   /**
22    * {@inheritdoc}
23    */
24   protected function setUp() {
25     parent::setUp();
26     $this->installConfig(['user', 'entity_test']);
27
28     // Give anonymous users permission to view test entities.
29     Role::load(RoleInterface::ANONYMOUS_ID)
30       ->grantPermission('view test entity')
31       ->save();
32   }
33
34   /**
35    * Tests entity render cache handling.
36    */
37   public function testEntityViewBuilderCache() {
38     /** @var \Drupal\Core\Render\RendererInterface $renderer */
39     $renderer = $this->container->get('renderer');
40     $cache_contexts_manager = \Drupal::service("cache_contexts_manager");
41     $cache = \Drupal::cache();
42
43     // Force a request via GET so we can get drupal_render() cache working.
44     $request = \Drupal::request();
45     $request_method = $request->server->get('REQUEST_METHOD');
46     $request->setMethod('GET');
47
48     $entity_test = $this->createTestEntity('entity_test');
49
50     // Test that new entities (before they are saved for the first time) do not
51     // generate a cache entry.
52     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
53     $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'The render array element of new (unsaved) entities is not cached, but does have cache tags set.');
54
55     // Get a fully built entity view render array.
56     $entity_test->save();
57     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
58     $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
59     $cid = implode(':', $cid_parts);
60     $bin = $build['#cache']['bin'];
61
62     // Mock the build array to not require the theme registry.
63     unset($build['#theme']);
64     $build['#markup'] = 'entity_render_test';
65
66     // Test that a cache entry is created.
67     $renderer->renderRoot($build);
68     $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
69
70     // Re-save the entity and check that the cache entry has been deleted.
71     $cache->set('kittens', 'Kitten data', Cache::PERMANENT, $build['#cache']['tags']);
72     $entity_test->save();
73     $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was saved.');
74     $this->assertFalse($cache->get('kittens'), 'The entity saving has invalidated cache tags.');
75
76     // Rebuild the render array (creating a new cache entry in the process) and
77     // delete the entity to check the cache entry is deleted.
78     unset($build['#printed']);
79     $renderer->renderRoot($build);
80     $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
81     $entity_test->delete();
82     $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.');
83
84     // Restore the previous request method.
85     $request->setMethod($request_method);
86   }
87
88   /**
89    * Tests entity render cache with references.
90    */
91   public function testEntityViewBuilderCacheWithReferences() {
92     /** @var \Drupal\Core\Render\RendererInterface $renderer */
93     $renderer = $this->container->get('renderer');
94     $cache_contexts_manager = \Drupal::service("cache_contexts_manager");
95
96     // Force a request via GET so we can get drupal_render() cache working.
97     $request = \Drupal::request();
98     $request_method = $request->server->get('REQUEST_METHOD');
99     $request->setMethod('GET');
100
101     // Create an entity reference field and an entity that will be referenced.
102     $this->createEntityReferenceField('entity_test', 'entity_test', 'reference_field', 'Reference', 'entity_test');
103     entity_get_display('entity_test', 'entity_test', 'full')->setComponent('reference_field', [
104       'type' => 'entity_reference_entity_view',
105       'settings' => ['link' => FALSE],
106     ])->save();
107     $entity_test_reference = $this->createTestEntity('entity_test');
108     $entity_test_reference->save();
109
110     // Get a fully built entity view render array for the referenced entity.
111     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full');
112     $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
113     $cid_reference = implode(':', $cid_parts);
114     $bin_reference = $build['#cache']['bin'];
115
116     // Mock the build array to not require the theme registry.
117     unset($build['#theme']);
118     $build['#markup'] = 'entity_render_test';
119     $renderer->renderRoot($build);
120
121     // Test that a cache entry was created for the referenced entity.
122     $this->assertTrue($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render element for the referenced entity has been cached.');
123
124     // Create another entity that references the first one.
125     $entity_test = $this->createTestEntity('entity_test');
126     $entity_test->reference_field->entity = $entity_test_reference;
127     $entity_test->save();
128
129     // Get a fully built entity view render array.
130     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
131     $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
132     $cid = implode(':', $cid_parts);
133     $bin = $build['#cache']['bin'];
134
135     // Mock the build array to not require the theme registry.
136     unset($build['#theme']);
137     $build['#markup'] = 'entity_render_test';
138     $renderer->renderRoot($build);
139
140     // Test that a cache entry is created.
141     $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
142
143     // Save the entity and verify that both cache entries have been deleted.
144     $entity_test_reference->save();
145     $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.');
146     $this->assertFalse($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render cache for the referenced entity has been cleared when the entity was deleted.');
147
148     // Restore the previous request method.
149     $request->setMethod($request_method);
150   }
151
152   /**
153    * Tests entity render cache toggling.
154    */
155   public function testEntityViewBuilderCacheToggling() {
156     $entity_test = $this->createTestEntity('entity_test');
157     $entity_test->save();
158
159     // Test a view mode in default conditions: render caching is enabled for
160     // the entity type and the view mode.
161     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
162     $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age', 'keys', 'bin'], 'A view mode with render cache enabled has the correct output (cache tags, keys, contexts, max-age and bin).');
163
164     // Test that a view mode can opt out of render caching.
165     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'test');
166     $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'A view mode with render cache disabled has the correct output (only cache tags, contexts and max-age).');
167
168     // Test that an entity type can opt out of render caching completely.
169     $this->installEntitySchema('entity_test_label');
170     $entity_test_no_cache = $this->createTestEntity('entity_test_label');
171     $entity_test_no_cache->save();
172     $build = $this->container->get('entity.manager')->getViewBuilder('entity_test_label')->view($entity_test_no_cache, 'full');
173     $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'An entity type can opt out of render caching regardless of view mode configuration, but always has cache tags, contexts and max-age set.');
174   }
175
176   /**
177    * Tests weighting of display components.
178    */
179   public function testEntityViewBuilderWeight() {
180     /** @var \Drupal\Core\Render\RendererInterface $renderer */
181     $renderer = $this->container->get('renderer');
182
183     // Set a weight for the label component.
184     entity_get_display('entity_test', 'entity_test', 'full')
185       ->setComponent('label', ['weight' => 20])
186       ->save();
187
188     // Create and build a test entity.
189     $entity_test = $this->createTestEntity('entity_test');
190     $view = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
191     $renderer->renderRoot($view);
192
193     // Check that the weight is respected.
194     $this->assertEqual($view['label']['#weight'], 20, 'The weight of a display component is respected.');
195   }
196
197   /**
198    * Creates an entity for testing.
199    *
200    * @param string $entity_type
201    *   The entity type.
202    *
203    * @return \Drupal\Core\Entity\EntityInterface
204    *   The created entity.
205    */
206   protected function createTestEntity($entity_type) {
207     $data = [
208       'bundle' => $entity_type,
209       'name' => $this->randomMachineName(),
210     ];
211     return $this->container->get('entity.manager')->getStorage($entity_type)->create($data);
212   }
213
214   /**
215    * Tests that viewing an entity without template does not specify #theme.
216    */
217   public function testNoTemplate() {
218     // Ensure that an entity type without explicit view builder uses the
219     // default.
220     $entity_type_manager = \Drupal::entityTypeManager();
221     $entity_type = $entity_type_manager->getDefinition('entity_test_base_field_display');
222     $this->assertTrue($entity_type->hasViewBuilderClass());
223     $this->assertEquals(EntityViewBuilder::class, $entity_type->getViewBuilderClass());
224
225     // Ensure that an entity without matching template does not have a #theme
226     // key.
227     $entity = $this->createTestEntity('entity_test');
228     $build = $entity_type_manager->getViewBuilder('entity_test')->view($entity);
229     $this->assertEquals($entity, $build['#entity_test']);
230     $this->assertFalse(array_key_exists('#theme', $build));
231   }
232
233 }