core/lib/Drupal/Core/Entity/EntityViewBuilder.php | 12 +- .../Tests/CustomBlockCacheTagsTest.php | 44 +++ .../Drupal/comment/Tests/CommentCacheTagsTest.php | 74 +++++ .../lib/Drupal/node/Tests/NodeCacheTagsTest.php | 56 ++++ .../system/Tests/Cache/PageCacheTagsTestBase.php | 56 ++++ .../Tests/Entity/EntityCacheTagsTestBase.php | 339 ++++++++++++++++++++ .../system/Tests/Entity/EntityViewBuilderTest.php | 8 +- .../Entity/EntityWithUriCacheTagsTestBase.php | 96 ++++++ .../modules/entity_test/entity_test.routing.yml | 8 + .../Controller/EntityTestController.php | 28 ++ .../Drupal/taxonomy/Tests/TermCacheTagsTest.php | 50 +++ .../lib/Drupal/taxonomy/Tests/TermTest.php | 99 ------ .../lib/Drupal/user/Tests/UserCacheTagsTest.php | 55 ++++ 13 files changed, 817 insertions(+), 108 deletions(-) diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 751f021..8ccf60a 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -143,19 +143,21 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco "#{$this->entityTypeId}" => $entity, '#view_mode' => $view_mode, '#langcode' => $langcode, + '#cache' => array( + 'tags' => array( + $this->entityTypeId . '_view' => TRUE, + $this->entityTypeId => array($entity->id()), + ), + ) ); // Cache the rendered output if permitted by the view mode and global entity // type configuration. if ($this->isViewModeCacheable($view_mode) && !$entity->isNew() && !isset($entity->in_preview) && $this->entityType->isRenderCacheable()) { - $return['#cache'] = array( + $return['#cache'] += array( 'keys' => array('entity_view', $this->entityTypeId, $entity->id(), $view_mode), 'granularity' => DRUPAL_CACHE_PER_ROLE, 'bin' => $this->cacheBin, - 'tags' => array( - $this->entityTypeId . '_view' => TRUE, - $this->entityTypeId => array($entity->id()), - ), ); } diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCacheTagsTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCacheTagsTest.php new file mode 100644 index 0000000..9d2139a --- /dev/null +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCacheTagsTest.php @@ -0,0 +1,44 @@ + 'Llama', + 'type' => 'basic', + 'body' => 'The name "llama" was adopted by European settlers from native Peruvians.', + )); + $custom_block->save(); + + return $custom_block; + } + +} diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentCacheTagsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentCacheTagsTest.php new file mode 100644 index 0000000..4af30b1 --- /dev/null +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentCacheTagsTest.php @@ -0,0 +1,74 @@ +grantPermission('access comments'); + $user_role->save(); + } + + /** + * {@inheritdoc} + */ + protected static function createEntity() { + // Create a "bar" bundle for the "entity_test" entity type and create. + $bundle = 'bar'; + entity_test_create_bundle($bundle, NULL, 'entity_test'); + + // Create a comment field on this bundle. + \Drupal::service('comment.manager')->addDefaultField('entity_test', 'bar'); + + // Create a "Camelids" test entity. + $entity_test = entity_create('entity_test', array( + 'name' => 'Camelids', + 'type' => 'bar', + )); + $entity_test->save(); + + // Create a "Llama" taxonomy term. + $comment = entity_create('comment', array( + 'subject' => 'Llama', + 'comment_body' => 'The name "llama" was adopted by European settlers from native Peruvians.', + 'entity_id' => $entity_test->id(), + 'entity_type' => 'entity_test', + 'field_name' => 'comment', + 'status' => \Drupal\comment\CommentInterface::PUBLISHED, + )); + $comment->save(); + + return $comment; + } + +} diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php new file mode 100644 index 0000000..1ab13c7 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php @@ -0,0 +1,56 @@ +grantPermission('bypass node access'); + $user_role->save(); + } + + /** + * {@inheritdoc} + */ + protected static function createEntity() { + // Create a "Llama" node. + $node = entity_create('node', array( + 'title' => 'Llama', + 'type' => 'article', + )); + $node->save(); + + return $node; + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php new file mode 100644 index 0000000..3bc1ee2 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php @@ -0,0 +1,56 @@ +set('cache.page.use_internal', 1); + $config->set('cache.page.max_age', 300); + $config->save(); + } + + /** + * Verify that when loading a given page, it's a page cache hit or miss. + * + * @param string $path + * The page at this path will be loaded. + * @param string $hit_or_miss + * 'HIT' if a page cache hit is expected, 'MISS' otherwise. + * + * @param array|FALSE $tags + * When expecting a page cache hit, you may optionally specify an array of + * expected cache tags. While FALSE, the cache tags will not be verified. + */ + protected function verifyPageCache($path, $hit_or_miss, $tags = FALSE) { + $this->drupalGet($path); + $message = String::format('Page cache @hit_or_miss for %path.', array('@hit_or_miss' => $hit_or_miss, '%path' => $path)); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), $hit_or_miss, $message); + + if ($hit_or_miss === 'HIT' && is_array($tags)) { + $cid_parts = array(url($path, array('absolute' => TRUE)), 'html'); + $cid = sha1(implode(':', $cid_parts)); + $cache_entry = \Drupal::cache('page')->get($cid); + $this->assertIdentical($cache_entry->tags, $tags); + } + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php new file mode 100644 index 0000000..27427bb --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php @@ -0,0 +1,339 @@ +grantPermission('view test entity'); + $user_role->save(); + + // Create an entity + $this->entity = static::createEntity(); + + // Create a referencing and a non-referencing entity. + list( + $this->referencing_entity, + $this->non_referencing_entity, + ) = static::createReferenceTestEntities($this->entity); + } + + /** + * Generates the standardized test info. + * + * @param string $entity_type_id + * The entity type ID whose cache tags to test. + * @param string $group + * The test group. + * + * @return array + * + * @see \Drupal\simpletest\TestBase::getInfo() + */ + protected static function generateStandardizedInfo($entity_type_id, $group) { + $label = \Drupal::entityManager() + ->getDefinition($entity_type_id) + ->getLabel(); + return array( + 'name' => "$label entity cache tags", + 'description' => "Test the $label entity's cache tags.", + 'group' => $group, + ); + } + + /** + * Creates the entity to be tested. + * + * @return \Drupal\Core\Entity\EntityInterface + * The entity to be tested. + */ + protected static function createEntity() { + // PHP doesn't allow us to declare this method as abstract protected static, + // so we simply throw an exception here if this has not been implemented by + // a child class. + throw new \RuntimeException(String::format('@class must implement \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity().', array( + '@class' => get_called_class(), + ))); + } + + /** + * Creates a referencing and a non-referencing entity for testing purposes. + * + * @param \Drupal\Core\Entity\EntityInterface $referenced_entity + * The entity that the referencing entity should reference. + * + * @return \Drupal\Core\Entity\EntityInterface[] + * An array containing a referencing entity and a non-referencing entity. + */ + protected static function createReferenceTestEntities($referenced_entity) { + // All referencing entities should be of the type 'entity_test'. + $entity_type = 'entity_test'; + + // Create a "foo" bundle for the given entity type. + $bundle = 'foo'; + entity_test_create_bundle($bundle, NULL, $entity_type); + + // Add a field of the given type to the given entity type's "foo" bundle. + $field_name = $referenced_entity->getEntityTypeId() . '_reference'; + entity_create('field_entity', array( + 'name' => $field_name, + 'entity_type' => $entity_type, + 'type' => 'entity_reference', + 'cardinality' => FieldDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => array( + 'target_type' => $referenced_entity->getEntityTypeId(), + ), + ))->save(); + entity_create('field_instance', array( + 'field_name' => $field_name, + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'settings' => array( + 'handler' => 'default', + 'handler_settings' => array( + 'target_bundles' => array( + $referenced_entity->bundle() => $referenced_entity->bundle(), + ), + 'sort' => array('field' => '_none'), + 'auto_create' => FALSE, + ), + ), + ))->save(); + entity_get_display($entity_type, $bundle, 'full') + ->setComponent($field_name, array('type' => 'entity_reference_label')) + ->save(); + + // Create an entity that does reference the entity being tested. + $label_key = \Drupal::entityManager()->getDefinition($entity_type)->getKey('label'); + $referencing_entity = entity_create($entity_type, array( + $label_key => 'Referencing ' . $entity_type, + 'status' => 1, + 'type' => $bundle, + $field_name => array('target_id' => $referenced_entity->id()), + )); + $referencing_entity->save(); + + // Create an entity that does not reference the entity being tested. + $non_referencing_entity = entity_create($entity_type, array( + $label_key => 'Non-referencing ' . $entity_type, + 'status' => 1, + 'type' => $bundle, + )); + $non_referencing_entity->save(); + + return array( + $referencing_entity, + $non_referencing_entity, + ); + } + + /** + * Tests cache tags presence and invalidation of the entity when referenced. + * + * Tests the following cache tags: + * - "_view:1" + * - ":" + * - "_view:1" + * * - ":" + */ + public function testReferencedEntity() { + $entity_type = $this->entity->getEntityTypeId(); + $referencing_entity_path = $this->referencing_entity->getSystemPath(); + $referencing_entity_type = $this->referencing_entity->getEntityTypeId(); + $non_referencing_entity_type = $this->non_referencing_entity->getEntityTypeId(); + $non_referencing_entity_path = $this->non_referencing_entity->getSystemPath(); + $listing_path = 'entity_test/list/' . $entity_type . '_reference/' . $entity_type . '/' . $this->entity->id(); + + // Generate the standardized entity cache tags. + $cache_tag = $entity_type . ':' . $this->entity->id(); + $view_cache_tag = $entity_type . '_view:1'; + + // Generate the cache tags for the (non) referencing entities. + $referencing_entity_cache_tags = array( + 'entity_test_view:1', + 'entity_test:' . $this->referencing_entity->id(), + // Includes the main entity's cache tags, since this entity references it. + $cache_tag, + $view_cache_tag + ); + $non_referencing_entity_cache_tags = array( + 'entity_test_view:1', + 'entity_test:' . $this->non_referencing_entity->id(), + ); + + + // Prime the page cache for the referencing entity. + $this->verifyPageCache($referencing_entity_path, 'MISS'); + + // Verify a cache hit, but also the presence of the correct cache tags. + $tags = array_merge(array('content:1'), $referencing_entity_cache_tags); + $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); + + // Also verify the existence of an entity render cache entry. + $cid = 'entity_view:entity_test:' . $this->referencing_entity->id() . ':full:stark:r.anonymous'; + $cache_entry = \Drupal::cache()->get($cid); + $this->assertIdentical($cache_entry->tags, $referencing_entity_cache_tags); + + + // Prime the page cache for the non-referencing entity. + $this->verifyPageCache($non_referencing_entity_path, 'MISS'); + + // Verify a cache hit, but also the presence of the correct cache tags. + $tags = array_merge(array('content:1'), $non_referencing_entity_cache_tags); + $this->verifyPageCache($non_referencing_entity_path, 'HIT', $tags); + + // Also verify the existence of an entity render cache entry. + $cid = 'entity_view:entity_test:' . $this->non_referencing_entity->id() . ':full:stark:r.anonymous'; + $cache_entry = \Drupal::cache()->get($cid); + $this->assertIdentical($cache_entry->tags, $non_referencing_entity_cache_tags); + + + + // Prime the page cache for the listing of referencing entities. + $this->verifyPageCache($listing_path, 'MISS'); + + // Verify a cache hit, but also the presence of the correct cache tags. + $tags = array_merge(array('content:1'), $referencing_entity_cache_tags); + $this->verifyPageCache($listing_path, 'HIT', $tags); + + + // Verify that after modifying the referenced entity, there is a cache miss + // for both the referencing entity, and the listing of referencing entities, + // but not for the non-referencing entity. + $this->pass("Test modification of referenced entity.", 'Debug'); + $this->entity->save(); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + + + // Verify that after modifying the referencing entity, there is a cache miss + // for both the referencing entity, and the listing of referencing entities, + // but not for the non-referencing entity. + $this->pass("Test modification of referencing entity.", 'Debug'); + $this->referencing_entity->save(); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + + + // Verify that after modifying the entity's full display, there is a cache + // miss for both the referencing entity, and the listing of referencing + // entities, but not for the non-referencing entity. + $this->pass("Test modification of referenced entity's 'full' display.", 'Debug'); + $entity_display = entity_get_display($entity_type, $this->entity->bundle(), 'full'); + $entity_display->save(); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + + + // Verify that after invalidating the entity's cache tag directly, there is + // a cache miss for both the referencing entity, and the listing of + // referencing entities, but not for the non-referencing entity. + $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); + Cache::invalidateTags(array($entity_type => array($this->entity->id()))); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + + + // Verify that after invalidating the generic entity type's view cache tag + // directly, there is a cache miss for both the referencing entity, and the + // listing of referencing entities, but not for the non-referencing entity. + $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); + Cache::invalidateTags(array($entity_type . '_view' => TRUE)); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + + + // Verify that after deleting the entity, there is a cache miss for both the + // referencing entity, and the listing of referencing entities, but not for + // the non-referencing entity. + $this->pass('Test deletion of referenced entity.', 'Debug'); + $this->entity->delete(); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + + // Verify cache hits. + $tags = array( + 'content:1', + 'entity_test_view:1', + 'entity_test:' . $this->referencing_entity->id(), + ); + $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); + $this->verifyPageCache($listing_path, 'HIT', array('content:1')); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewBuilderTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewBuilderTest.php index ab1c086..2d0369b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewBuilderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewBuilderTest.php @@ -48,7 +48,7 @@ public function testEntityViewBuilderCache() { // Test that new entities (before they are saved for the first time) do not // generate a cache entry. $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $this->assertFalse(isset($build['#cache']), 'The render array element of new (unsaved) entities is not cached.'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == array('tags'), 'The render array element of new (unsaved) entities is not cached, but does have cache tags set.'); // Get a fully built entity view render array. $entity_test->save(); @@ -144,17 +144,17 @@ public function testEntityViewBuilderCacheToggling() { // Test a view mode in default conditions: render caching is enabled for // the entity type and the view mode. $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $this->assertTrue(isset($build['#cache']), 'A view mode with render cache enabled has the correct output.'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == array('tags', 'keys', 'granularity', 'bin') , 'A view mode with render cache enabled has the correct output (cache tags, keys, granularity and bin).'); // Test that a view mode can opt out of render caching. $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'test'); - $this->assertFalse(isset($build['#cache']), 'A view mode with render cache disabled has the correct output.'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == array('tags'), 'A view mode with render cache disabled has the correct output (only cache tags).'); // Test that an entity type can opt out of render caching completely. $entity_test_no_cache = $this->createTestEntity('entity_test_label'); $entity_test_no_cache->save(); $build = $this->container->get('entity.manager')->getViewBuilder('entity_test_label')->view($entity_test_no_cache, 'full'); - $this->assertFalse(isset($build['#cache']), 'An entity type can opt out of render caching regardless of view mode configuration.'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == array('tags'), 'An entity type can opt out of render caching regardless of view mode configuration, but always has cache tags set.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php new file mode 100644 index 0000000..7472ce3 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php @@ -0,0 +1,96 @@ +_view:1" + * - ":" + */ + public function testEntityUri() { + $entity_path = $this->entity->getSystemPath(); + $entity_type = $this->entity->getEntityTypeId(); + + // Generate the standardized entity cache tags. + $cache_tag = $entity_type . ':' . $this->entity->id(); + $view_cache_tag = $entity_type . '_view:1'; + + + // Prime the page cache. + $this->verifyPageCache($entity_path, 'MISS'); + + // Verify a cache hit, but also the presence of the correct cache tags. + $tags = array('content:1', $view_cache_tag, $cache_tag); + $this->verifyPageCache($entity_path, 'HIT'); + + // Also verify the existence of an entity render cache entry, if this entity + // type supports render caching. + if (\Drupal::entityManager()->getDefinition($entity_type)->isRenderCacheable()) { + $cid = 'entity_view:' . $entity_type . ':' . $this->entity->id() . ':full:stark:r.anonymous'; + $cache_entry = \Drupal::cache()->get($cid); + $this->assertIdentical($cache_entry->tags, array($view_cache_tag, $cache_tag)); + } + + // Verify that after modifying the entity, there is a cache miss. + $this->pass("Test modification of entity.", 'Debug'); + $this->entity->save(); + $this->verifyPageCache($entity_path, 'MISS'); + + // Verify a cache hit. + $this->verifyPageCache($entity_path, 'HIT'); + + + // Verify that after modifying the entity's "full" display, there is a cache + // miss. + $this->pass("Test modification of entity's 'full' display.", 'Debug'); + $entity_display = entity_get_display($entity_type, $this->entity->bundle(), 'full'); + $entity_display->save(); + $this->verifyPageCache($entity_path, 'MISS'); + + // Verify a cache hit. + $this->verifyPageCache($entity_path, 'HIT'); + + + // Verify that after invalidating the entity's cache tag directly, there is + // a cache miss. + $this->pass("Test invalidation of entity's cache tag.", 'Debug'); + Cache::invalidateTags(array($entity_type => array($this->entity->id()))); + $this->verifyPageCache($entity_path, 'MISS'); + + // Verify a cache hit. + $this->verifyPageCache($entity_path, 'HIT'); + + + // Verify that after invalidating the generic entity type's view cache tag + // directly, there is a cache miss. + $this->pass("Test invalidation of entity's 'view' cache tag.", 'Debug'); + Cache::invalidateTags(array($entity_type . '_view' => TRUE)); + $this->verifyPageCache($entity_path, 'MISS'); + + // Verify a cache hit. + $this->verifyPageCache($entity_path, 'HIT'); + + + // Verify that after deleting the entity, there is a cache miss. + $this->pass('Test deletion of entity.', 'Debug'); + $this->entity->delete(); + $this->verifyPageCache($entity_path, 'MISS'); + $this->assertResponse(404); + } + +} diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml index 6905f23..55cc8bf 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml +++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml @@ -23,5 +23,13 @@ entity_test.render_no_view_mode: requirements: _access: 'TRUE' +entity_test.list_referencing_entities: + path: '/entity_test/list/{entity_reference_field_name}/{referenced_entity_type}/{referenced_entity_id}' + defaults: + _content: '\Drupal\entity_test\Controller\EntityTestController::listReferencingEntities' + _title: 'List entity_test entities referencing the given entity' + requirements: + _access: 'TRUE' + route_callbacks: - '\Drupal\entity_test\Routing\EntityTestRoutes::routes' diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php index a6a42ee..126f08b 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Controller/EntityTestController.php @@ -37,4 +37,32 @@ public function testAdmin() { return ''; } + /** + * List entity_test entities referencing the given entity + * + * @param string $entity_reference_field_name + * The name of the entity_reference field to use in the query. + * @param string $referenced_entity_type + * The type of the entity being referenced. + * @param int $referenced_entity_id + * The ID of the entity being referenced. + * + * @return array + * A renderable array. + */ + public function listReferencingEntities($entity_reference_field_name, $referenced_entity_type, $referenced_entity_id) { + // Early return if the referenced entity does not exist (or is deleted). + if (entity_load($referenced_entity_type, $referenced_entity_id) === NULL) { + return array(); + } + + $query = \Drupal::service('entity.query') + ->get('entity_test') + ->condition($entity_reference_field_name . '.target_id', $referenced_entity_id); + + $entities = entity_load_multiple('entity_test', $query->execute()); + + return entity_view_multiple($entities, 'full'); + } + } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermCacheTagsTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermCacheTagsTest.php new file mode 100644 index 0000000..627da44 --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermCacheTagsTest.php @@ -0,0 +1,50 @@ + 'Camelids', + 'vid' => 'camelids', + )); + $vocabulary->save(); + + // Create a "Llama" taxonomy term. + $term = entity_create('taxonomy_term', array( + 'name' => 'Llama', + 'vid' => $vocabulary->id(), + )); + $term->save(); + + return $term; + } + +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index baf5158..5a53567 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -7,7 +7,6 @@ namespace Drupal\taxonomy\Tests; -use Drupal\Core\Cache\Cache; use Drupal\Component\Utility\Json; use Drupal\Component\Utility\Tags; use Drupal\Core\Field\FieldDefinitionInterface; @@ -544,102 +543,4 @@ function testReSavingTags() { $this->assertRaw($term->label(), 'Term is displayed after saving the node with no changes.'); } - /** - * Tests cache tags presence and invalidation. - * - * Tests the following cache tags: - * - "taxonomy_term_view:1" - * - "taxonomy_term:" - */ - public function testCacheTagsPresenceAndInvalidation() { - // Enable page caching. - $config = \Drupal::config('system.performance'); - $config->set('cache.page.use_internal', 1); - $config->set('cache.page.max_age', 300); - $config->save(); - - // Page caching only works for anonymous users. - $this->drupalLogout(); - - // Create a "Camelids" vocabulary - $vocabulary = entity_create('taxonomy_vocabulary', array( - 'name' => 'camelids', - 'vid' => 'camelids', - )); - $vocabulary->save(); - - // Create a "Llama" taxonomy term. - $term = entity_create('taxonomy_term', array( - 'name' => 'Llama', - 'vid' => $vocabulary->id(), - )); - $term->save(); - $term_path = 'taxonomy/term/' . $term->id(); - - // Prime the page cache. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); - - // Verify a cache hit, but also the presence of the correct cache tags. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); - $cid_parts = array(url($term_path, array('absolute' => TRUE)), 'html'); - $cid = sha1(implode(':', $cid_parts)); - $cache_entry = \Drupal::cache('page')->get($cid); - $this->assertIdentical($cache_entry->tags, array('content:1', 'taxonomy_term_view:1', 'taxonomy_term:' . $term->id())); - - // Also verify the existence of an entity render cache entry. - $cid = 'entity_view:taxonomy_term:' . $term->id() . ':full:stark:r.anonymous'; - $cache_entry = \Drupal::cache()->get($cid); - $this->assertIdentical($cache_entry->tags, array('taxonomy_term_view:1', 'taxonomy_term:' . $term->id())); - - // The "Llama" term is modified. - $term->description = 'The name "llama" was adopted by European settlers from native Peruvians.'; - $term->save(); - - // Verify that after modifying the term, there is a cache miss. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); - - // Verify a cache hit. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); - - // Verify that after modifying the Term entity's default display, there is - // a cache miss. - $entity_display = entity_create('entity_display', array( - 'targetEntityType' => 'taxonomy_term', - 'bundle' => 'camelids', - 'mode' => 'default', - 'status' => TRUE, - )); - $entity_display->save(); - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); - - // Verify a cache hit. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); - - // Verify that after invalidating the taxonomy term's cache tag directly, - // there is a cache miss. - Cache::invalidateTags(array('taxonomy_term' => array($term->id()))); - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); - - // Verify a cache hit. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); - - // Verify that after invalidating the generic taxonomy term view cache tag - // directly, there is a cache miss. - Cache::invalidateTags(array('taxonomy_term_view' => TRUE)); - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); - - // Verify a cache hit. - $this->drupalGet($term_path); - $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT'); - } - } diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCacheTagsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCacheTagsTest.php new file mode 100644 index 0000000..d9e1c8f --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/UserCacheTagsTest.php @@ -0,0 +1,55 @@ +grantPermission('access user profiles'); + $user_role->save(); + } + + /** + * {@inheritdoc} + */ + protected static function createEntity() { + // Create a "Llama" user. + $user = entity_create('user', array( + 'name' => 'Llama', + )); + $user->save(); + + return $user; + } + +}