/** * Test simple and editorial translation workflows. */ function testWorkflows() { // Test workflows for the editor. $expected_status = ['edit' => 200, 'delete' => 200, 'overview' => 403, 'add_translation' => 403, 'edit_translation' => 403, 'delete_translation' => 403]; $this->doTestWorkflows($this->editor, $expected_status); // Test workflows for the translator. $expected_status = ['edit' => 403, 'delete' => 403, 'overview' => 200, 'add_translation' => 200, 'edit_translation' => 200, 'delete_translation' => 200]; $this->doTestWorkflows($this->translator, $expected_status); // Test workflows for the admin. $expected_status = ['edit' => 200, 'delete' => 200, 'overview' => 200, 'add_translation' => 200, 'edit_translation' => 403, 'delete_translation' => 403]; $this->doTestWorkflows($this->administrator, $expected_status); // Check that translation permissions allow the associated operations. $ops = array('create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete')); $translations_url = $this->entity->urlInfo('drupal:content-translation-overview'); foreach ($ops as $current_op => $item) { $user = $this->drupalCreateUser(array($this->getTranslatePermission(), "{$current_op} content translations")); $this->drupalLogin($user); $this->drupalGet($translations_url); // Make sure that the user.permissions cache context and the cache tags // for the entity are present. $this->assertCacheContext('user.permissions'); foreach ($this->entity->getCacheTags() as $cache_tag) { $this->assertCacheTag($cache_tag); } foreach ($ops as $op => $label) { if ($op != $current_op) { $this->assertNoLink($label, format_string('No %op link found.', array('%op' => $label))); } else { $this->assertLink($label, 0, format_string('%op link found.', array('%op' => $label))); } } } }
/** * Tests the label formatter. */ public function testLabelFormatter() { /** @var \Drupal\Core\Render\RendererInterface $renderer */ $renderer = $this->container->get('renderer'); $formatter = 'entity_reference_label'; // The 'link' settings is TRUE by default. $build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter); $expected_field_cacheability = ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT]; $this->assertEqual($build['#cache'], $expected_field_cacheability, 'The field render array contains the entity access cacheability metadata'); $expected_item_1 = array('#type' => 'link', '#title' => $this->referencedEntity->label(), '#url' => $this->referencedEntity->urlInfo(), '#options' => $this->referencedEntity->urlInfo()->getOptions(), '#cache' => array('contexts' => ['user.permissions'], 'tags' => $this->referencedEntity->getCacheTags())); $this->assertEqual($renderer->renderRoot($build[0]), $renderer->renderRoot($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter)); $this->assertEqual(CacheableMetadata::createFromRenderArray($build[0]), CacheableMetadata::createFromRenderArray($expected_item_1)); // The second referenced entity is "autocreated", therefore not saved and // lacking any URL info. $expected_item_2 = array('#plain_text' => $this->unsavedReferencedEntity->label(), '#cache' => array('contexts' => ['user.permissions'], 'tags' => $this->unsavedReferencedEntity->getCacheTags(), 'max-age' => Cache::PERMANENT)); $this->assertEqual($build[1], $expected_item_2, sprintf('The render array returned by the %s formatter is correct for an item with a unsaved entity.', $formatter)); // Test with the 'link' setting set to FALSE. $build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter, array('link' => FALSE)); $this->assertEqual($build[0]['#plain_text'], $this->referencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter)); $this->assertEqual($build[1]['#plain_text'], $this->unsavedReferencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter)); // Test an entity type that doesn't have any link templates, which means // \Drupal\Core\Entity\EntityInterface::urlInfo() will throw an exception // and the label formatter will output only the label instead of a link. $field_storage_config = FieldStorageConfig::loadByName($this->entityType, $this->fieldName); $field_storage_config->setSetting('target_type', 'entity_test_label'); $field_storage_config->save(); $referenced_entity_with_no_link_template = EntityTestLabel::create(array('name' => $this->randomMachineName())); $referenced_entity_with_no_link_template->save(); $build = $this->buildRenderArray([$referenced_entity_with_no_link_template], $formatter, array('link' => TRUE)); $this->assertEqual($build[0]['#plain_text'], $referenced_entity_with_no_link_template->label(), sprintf('The markup returned by the %s formatter is correct for an entity type with no valid link template.', $formatter)); }
/** * Tests the entity formatter. */ public function testEntityFormatter() { $formatter = 'entity_reference_entity_view'; $field_name = $this->fieldName; // Create the entity that will have the entity reference field. $referencing_entity = entity_create($this->entityType, array('name' => $this->randomMachineName())); $referencing_entity->save(); $referencing_entity->{$field_name}->entity = $this->referencedEntity; $referencing_entity->{$field_name}->access = TRUE; // Build the renderable array for the entity reference field. $items = $referencing_entity->get($field_name); $build = $items->view(array('type' => $formatter)); $expected_rendered_name_field = '<div class="field field-entity-test--name field-name-name field-type-string field-label-hidden"> <div class="field-items"> <div class="field-item">' . $this->referencedEntity->label() . '</div> </div> </div> '; $expected_rendered_body_field = '<div class="field field-entity-test--body field-name-body field-type-text field-label-above"> <div class="field-label">Body</div> <div class="field-items"> <div class="field-item"><p>Hello, world!</p></div> </div> </div> '; drupal_render($build[0]); $this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() . $expected_rendered_name_field . $expected_rendered_body_field, format_string('The markup returned by the @formatter formatter is correct.', array('@formatter' => $formatter))); $expected_cache_tags = Cache::mergeTags(\Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(), $this->referencedEntity->getCacheTags(), FilterFormat::load('full_html')->getCacheTags()); $this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter))); }
/** * Tests the label formatter. */ public function testLabelFormatter() { $formatter = 'entity_reference_label'; // The 'link' settings is TRUE by default. $build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter); $expected_item_1 = array('#type' => 'link', '#title' => $this->referencedEntity->label(), '#url' => $this->referencedEntity->urlInfo(), '#options' => $this->referencedEntity->urlInfo()->getOptions(), '#cache' => array('tags' => $this->referencedEntity->getCacheTags())); $this->assertEqual(drupal_render($build[0]), drupal_render($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter)); // The second referenced entity is "autocreated", therefore not saved and // lacking any URL info. $expected_item_2 = array('#markup' => $this->unsavedReferencedEntity->label(), '#cache' => array('tags' => $this->unsavedReferencedEntity->getCacheTags())); $this->assertEqual($build[1], $expected_item_2, sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter)); // Test with the 'link' setting set to FALSE. $build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter, array('link' => FALSE)); $this->assertEqual($build[0]['#markup'], $this->referencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter)); $this->assertEqual($build[1]['#markup'], $this->unsavedReferencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter)); // Test an entity type that doesn't have any link templates, which means // \Drupal\Core\Entity\EntityInterface::urlInfo() will throw an exception // and the label formatter will output only the label instead of a link. $field_storage_config = FieldStorageConfig::loadByName($this->entityType, $this->fieldName); $field_storage_config->setSetting('target_type', 'entity_test_label'); $field_storage_config->save(); $referenced_entity_with_no_link_template = entity_create('entity_test_label', array('name' => $this->randomMachineName())); $referenced_entity_with_no_link_template->save(); $build = $this->buildRenderArray([$referenced_entity_with_no_link_template], $formatter, array('link' => TRUE)); $this->assertEqual($build[0]['#markup'], $referenced_entity_with_no_link_template->label(), sprintf('The markup returned by the %s formatter is correct for an entity type with no valid link template.', $formatter)); }
/** * {@inheritdoc} */ protected function calculateXmlCacheTags() { // Add tags for the entity that this XML comes from. $entity_tags = $this->entity instanceof CacheableDependencyInterface ? $this->entity->getCacheTags() : array(); // Also fetch the tags from the display configuration as that is where our // gallery-specific settings are stored (so changes there should also // invalidate the XML). $display = entity_get_display($this->entityType, $this->entity->bundle(), $this->displayName); $display_tags = array(); if ($display instanceof CacheableDependencyInterface) { $display_tags = $display->getCacheTags(); // If this is not a custom display then we need to also include the // default display cache tags as Drupal may reference this display // elsewhere by the "default" label. if (!$display->status() || $display->isNew()) { $display_default = entity_get_display($this->entityType, $this->entity->bundle(), 'default'); if ($display_default instanceof CacheableDependencyInterface) { $display_tags = Cache::mergeTags($display_tags, $display_default->getCacheTags()); } } } return Cache::mergeTags($entity_tags, $display_tags); }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - entity type view cache tag: "<entity type>_view" * - entity cache tag: "<entity type>:<entity ID>" * - entity type list cache tag: "<entity type>_list" * - referencing entity type view cache tag: "<referencing entity type>_view" * - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_url = $this->referencingEntity->urlInfo('canonical'); $non_referencing_entity_url = $this->nonReferencingEntity->urlInfo('canonical'); $listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', ['entity_reference_field_name' => $entity_type . '_reference', 'referenced_entity_type' => $entity_type, 'referenced_entity_id' => $this->entity->id()]); $empty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_empty', ['entity_type_id' => $entity_type]); $nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]); // The default cache contexts for rendered entities. $default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions']; $entity_cache_contexts = $default_cache_contexts; $page_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]); // Cache tags present on every rendered page. // 'user.permissions' is a required cache context, and responses that vary // by this cache context when requested by anonymous users automatically // also get this cache tag, to ensure correct invalidation. $page_cache_tags = Cache::mergeTags(['rendered'], ['config:user.role.anonymous']); // If the block module is used, the Block page display variant is used, // which adds the block config entity type's list cache tags. $page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []); $page_cache_tags_referencing_entity = in_array('user.permissions', $this->getAccessCacheContextsForEntity($this->referencingEntity)) ? ['config:user.role.anonymous'] : []; $view_cache_tag = array(); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags(); } $context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($entity_cache_contexts); $cache_context_tags = $context_metadata->getCacheTags(); // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); // Includes the main entity's cache tags, since this entity references it. $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->entity->getCacheTags()); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity)); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $view_cache_tag); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $cache_context_tags); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']); $non_referencing_entity_cache_tags = Cache::mergeTags($this->nonReferencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $non_referencing_entity_cache_tags = Cache::mergeTags($non_referencing_entity_cache_tags, ['rendered']); // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $this->getAdditionalCacheTagsForEntityListing($this->entity)); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags); $expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity); $this->verifyPageCache($referencing_entity_url, 'HIT', $expected_tags); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->referencingEntity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $access_cache_contexts = $this->getAccessCacheContextsForEntity($this->entity); $additional_cache_contexts = $this->getAdditionalCacheContextsForEntity($this->referencingEntity); $redirected_cid = NULL; if (count($access_cache_contexts) || count($additional_cache_contexts)) { $cache_contexts = Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts); $cache_contexts = Cache::mergeContexts($cache_contexts, $access_cache_contexts); $redirected_cid = $this->createCacheId($cache_keys, $cache_contexts); $context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $context_metadata->getCacheTags()); } $this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($non_referencing_entity_url, 'HIT', Cache::mergeTags($non_referencing_entity_cache_tags, $page_cache_tags)); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->nonReferencingEntity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $this->verifyRenderCache($cid, $non_referencing_entity_cache_tags); $this->pass("Test listing of referencing entities.", 'Debug'); // Prime the page cache for the listing of referencing entities. $this->verifyPageCache($listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags); $expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity); $this->verifyPageCache($listing_url, 'HIT', $expected_tags); $this->pass("Test empty listing.", 'Debug'); // Prime the page cache for the empty listing. $this->verifyPageCache($empty_entity_listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); $this->pass("Test listing containing referenced entity.", 'Debug'); // Prime the page cache for the listing containing the referenced entity. $this->verifyPageCache($nonempty_entity_listing_url, 'MISS', $nonempty_entity_listing_cache_tags); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); // Verify that after modifying the referenced entity, there is a cache miss // for every route except the one for the non-referencing entity. $this->pass("Test modification of referenced entity.", 'Debug'); $this->entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for every route except the ones for the non-referencing entity and the // empty entity listing. $this->pass("Test modification of referencing entity.", 'Debug'); $this->referencingEntity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss only for the non-referencing entity route. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->nonReferencingEntity->save(); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_url, 'HIT'); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for any other routes. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for any other routes. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type_id, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); } else { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } } if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = FieldStorageConfig::load($field_storage_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field = FieldConfig::load($field_name); $field->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after invalidating the entity's cache tag directly, there is // a cache miss for every route except the ones for the non-referencing // entity and the empty entity listing. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTagsToInvalidate()); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after invalidating the entity's list cache tag directly, // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); if (!empty($view_cache_tag)) { // 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 other routes. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after deleting the entity, there is a cache miss for every // route except for the non-referencing entity one. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); $this->verifyPageCache($listing_url, 'HIT', $page_cache_tags); $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); }
/** * Convenience method, adds the entity's cache tag. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity whose cache tag to set on the access result. * * @return $this */ public function cacheUntilEntityChanges(EntityInterface $entity) { $this->addCacheTags($entity->getCacheTags()); return $this; }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - entity type view cache tag: "<entity type>_view" * - entity cache tag: "<entity type>:<entity ID>" * - entity type list cache tag: "<entity type>_list" * - referencing entity type view cache tag: "<referencing entity type>_view" * - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_path = $this->referencing_entity->getSystemPath(); $non_referencing_entity_path = $this->non_referencing_entity->getSystemPath(); $listing_path = 'entity_test/list/' . $entity_type . '_reference/' . $entity_type . '/' . $this->entity->id(); $empty_entity_listing_path = 'entity_test/list_empty/' . $entity_type; $nonempty_entity_listing_path = 'entity_test/list_labels_alphabetically/' . $entity_type; $render_cache_tags = array('rendered'); $theme_cache_tags = array('theme:stark', 'theme_global_settings'); $view_cache_tag = array(); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags(); } // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntity($this->entity), $view_cache_tag); $non_referencing_entity_cache_tags = Cache::mergeTags($this->non_referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $theme_cache_tags, $render_cache_tags); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntityListing($this->entity), $theme_cache_tags, $render_cache_tags); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $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:' . date_default_timezone_get(); $tags = Cache::mergeTags($render_cache_tags, $referencing_entity_cache_tags); $this->verifyRenderCache($cid, $tags); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $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:' . date_default_timezone_get(); $tags = Cache::mergeTags($render_cache_tags, $non_referencing_entity_cache_tags); $this->verifyRenderCache($cid, $tags); $this->pass("Test listing of referencing entities.", 'Debug'); // 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 = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($listing_path, 'HIT', $tags); $this->pass("Test empty listing.", 'Debug'); // Prime the page cache for the empty listing. $this->verifyPageCache($empty_entity_listing_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($empty_entity_listing_path, 'HIT', $empty_entity_listing_cache_tags); $this->pass("Test listing containing referenced entity.", 'Debug'); // Prime the page cache for the listing containing the referenced entity. $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($nonempty_entity_listing_path, 'HIT', $nonempty_entity_listing_cache_tags); // Verify that after modifying the referenced entity, there is a cache miss // for every route except the one 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($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for every route except the ones for the non-referencing entity and the // empty entity listing. $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($nonempty_entity_listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss only for the non-referencing entity route. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->non_referencing_entity->save(); $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_path, 'HIT'); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for any other routes. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } $bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType(); if ($bundle_entity_type !== 'bundle') { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for any other routes. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); } else { $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); } // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); } } if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = entity_load('field_storage_config', $field_storage_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $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 a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field = entity_load('field_config', $field_name); $field->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $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 every route except the ones for the non-referencing // entity and the empty entity listing. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTags()); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify that after invalidating the entity's list cache tag directly, // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); if (!empty($view_cache_tag)) { // 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 other routes. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_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 every // route except for the the non-referencing entity one. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags); $this->verifyPageCache($listing_path, 'HIT', $tags); $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_path, 'HIT', $tags); $tags = Cache::mergeTags($tags, $this->getAdditionalCacheTagsForEntityListing()); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT', $tags); }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - entity type view cache tag: "<entity type>_view" * - entity cache tag: "<entity type>:<entity ID>" * - entity type list cache tag: "<entity type>_list" * - referencing entity type view cache tag: "<referencing entity type>_view" * - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_url = $this->referencing_entity->urlInfo('canonical'); $non_referencing_entity_url = $this->non_referencing_entity->urlInfo('canonical'); $listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', ['entity_reference_field_name' => $entity_type . '_reference', 'referenced_entity_type' => $entity_type, 'referenced_entity_id' => $this->entity->id()]); $empty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_empty', ['entity_type_id' => $entity_type]); $nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]); // The default cache contexts for rendered entities. $entity_cache_contexts = ['theme', 'user.roles']; // Cache tags present on every rendered page. $page_cache_tags = Cache::mergeTags(['rendered'], \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []); $view_cache_tag = array(); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags(); } // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntity($this->entity), $view_cache_tag, ['rendered']); $non_referencing_entity_cache_tags = Cache::mergeTags($this->non_referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), ['rendered']); // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntityListing($this->entity), $page_cache_tags); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->referencing_entity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $redirected_cid = $this->createRedirectedCacheId($cache_keys, $entity_cache_contexts); $this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($non_referencing_entity_url, 'HIT', Cache::mergeTags($non_referencing_entity_cache_tags, $page_cache_tags)); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->non_referencing_entity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $this->verifyRenderCache($cid, $non_referencing_entity_cache_tags); $this->pass("Test listing of referencing entities.", 'Debug'); // Prime the page cache for the listing of referencing entities. $this->verifyPageCache($listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($listing_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); $this->pass("Test empty listing.", 'Debug'); // Prime the page cache for the empty listing. $this->verifyPageCache($empty_entity_listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); $this->pass("Test listing containing referenced entity.", 'Debug'); // Prime the page cache for the listing containing the referenced entity. $this->verifyPageCache($nonempty_entity_listing_url, 'MISS', $nonempty_entity_listing_cache_tags); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); // Verify that after modifying the referenced entity, there is a cache miss // for every route except the one for the non-referencing entity. $this->pass("Test modification of referenced entity.", 'Debug'); $this->entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for every route except the ones for the non-referencing entity and the // empty entity listing. $this->pass("Test modification of referencing entity.", 'Debug'); $this->referencing_entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss only for the non-referencing entity route. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->non_referencing_entity->save(); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_url, 'HIT'); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for any other routes. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } $bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType(); if ($bundle_entity_type !== 'bundle') { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for any other routes. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); } else { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } } if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = FieldStorageConfig::load($field_storage_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field = FieldConfig::load($field_name); $field->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after invalidating the entity's cache tag directly, there is // a cache miss for every route except the ones for the non-referencing // entity and the empty entity listing. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTags()); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after invalidating the entity's list cache tag directly, // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); if (!empty($view_cache_tag)) { // 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 other routes. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after deleting the entity, there is a cache miss for every // route except for the non-referencing entity one. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), ['rendered']); $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); $this->verifyPageCache($listing_url, 'HIT', $page_cache_tags); $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing(), $page_cache_tags)); }