commit 9903cb6b098283a730d624590ada806dcdb4a508 Author: Andrei Mateescu Date: Fri Feb 22 10:05:43 2013 +0100 More work on this and some text fixes. diff --git a/core/includes/common.inc b/core/includes/common.inc index 14e4357..cb9beef 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -5769,7 +5769,7 @@ function drupal_render_cache_set(&$markup, $elements) { } $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache'; $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CacheBackendInterface::CACHE_PERMANENT; - $tags = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array(); + $tags = drupal_render_collect_cache_tags($elements, TRUE); cache($bin)->set($cid, $data, $expire, $tags); } @@ -5817,6 +5817,57 @@ function drupal_render_collect_attached($elements, $return = FALSE) { } /** + * Collects cache tags for an element and its children into a single array. + * + * When caching elements, it is necessary to collect all cache tags into a + * single array, from both the element itself and all child elements. This + * allows items to be invalidated based on all tags attached to the content + * they're constituted from. + * + * @param array $elements + * The element to collect cache tags from. + * @param bool $return + * Whether to return the attached elements and reset the internal static. + * + * @return array + * The cache tags array for this element and its descendants. + */ +function drupal_render_collect_cache_tags($elements, $return = FALSE) { + $tags = &drupal_static(__FUNCTION__, array()); + + // Collect all cache tags for this element. + if (isset($elements['#cache']['tags'])) { + foreach ($elements['#cache']['tags'] as $namespace => $values) { + if (is_array($values)) { + foreach ($values as $value) { + if (!isset($tags[$namespace][$value])) { + $tags[$namespace][$value] = $value; + } + } + } + else { + if (!isset($tags[$namespace])) { + $tags[$namespace] = $values; + } + } + } + } + if ($children = element_children($elements)) { + foreach ($children as $child) { + drupal_render_collect_cache_tags($elements[$child]); + } + } + + // If this was the first call to the function, return all attached elements + // and reset the static cache. + if ($return) { + $return = $tags; + $tags = array(); + return $return; + } +} + +/** * Prepares an element for caching based on a query. * * This smart caching strategy saves Drupal from querying and rendering to HTML diff --git a/core/includes/entity.inc b/core/includes/entity.inc index b002112..acc5b65 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -105,6 +105,9 @@ function entity_get_view_modes($entity_type = NULL) { else { $view_modes = module_invoke_all('entity_view_mode_info'); foreach ($view_modes as $type => $entity_info) { + $view_modes[$type] += array( + 'cache_default' => TRUE, + ); foreach ($entity_info as $view_mode => $view_mode_info) { $view_modes[$type][$view_mode] += array( 'custom_settings' => FALSE, @@ -599,7 +602,7 @@ function entity_render_controller($entity_type) { function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) { $render_controller = drupal_container()->get('plugin.manager.entity')->getRenderController($entity->entityType()); if ($reset) { - $render_controller->resetCache(array($entity->id())); + $render_controller->resetCache(array($entity)); } return $render_controller->view($entity, $view_mode, $langcode); } @@ -625,11 +628,7 @@ function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $res function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) { $render_controller = drupal_container()->get('plugin.manager.entity')->getRenderController(reset($entities)->entityType()); if ($reset) { - $ids = array(); - foreach ($entities as $entity) { - $ids[] = $entity->id(); - } - $render_controller->resetCache($ids); + $render_controller->resetCache($entities); } return $render_controller->viewMultiple($entities, $view_mode, $langcode); } diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index 2d37ffb..face9db 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -169,6 +169,21 @@ public function resetCache(array $ids = NULL) { } /** + * Resets the entity render cache. + * + * @param array|null $entities + * (optional) If specified, the cache is reset for the given entities only. + */ + protected function resetRenderCache(array $entities = NULL) { + try { + drupal_container()->get('plugin.manager.entity')->getRenderController($this->entityType)->resetCache($entities); + } + catch (\Exception $e) { + // Nothing to do if the entity type doesn't have a render controller. + } + } + + /** * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::load(). */ public function load(array $ids = NULL) { @@ -530,7 +545,7 @@ public function save(EntityInterface $entity) { } $this->resetCache(array($entity->id())); // Reset the render cache as well. - drupal_container()->get('plugin.manager.entity')->getRenderController($this->entityType)->resetCache(array($entity->id())); + $this->resetRenderCache(array($entity)); $this->postSave($entity, TRUE); $this->invokeHook('update', $entity); @@ -542,6 +557,8 @@ public function save(EntityInterface $entity) { } // Reset general caches, but keep caches specific to certain entities. $this->resetCache(array()); + // Reset the render cache as well. + $this->resetRenderCache(array($entity)); $entity->enforceIsNew(FALSE); $this->postSave($entity, FALSE); diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php index 61f6256..031edb8 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php @@ -298,7 +298,7 @@ public function save(EntityInterface $entity) { } $this->resetCache(array($entity->id())); // Reset the render cache as well. - drupal_container()->get('plugin.manager.entity')->getRenderController($this->entityType)->resetCache(array($entity->id())); + $this->resetRenderCache(array($entity)); $this->postSave($entity, TRUE); $this->invokeHook('update', $entity); @@ -316,6 +316,8 @@ public function save(EntityInterface $entity) { // Reset general caches, but keep caches specific to certain entities. $this->resetCache(array()); + // Reset the render cache as well. + $this->resetRenderCache(array($entity)); $entity->enforceIsNew(FALSE); $this->postSave($entity, FALSE); diff --git a/core/lib/Drupal/Core/Entity/EntityRenderController.php b/core/lib/Drupal/Core/Entity/EntityRenderController.php index 2c335a8..a7297bf 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderController.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderController.php @@ -176,10 +176,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la $build[$key]['#weight'] = $weight++; // Cache the rendered output if permitted by the view mode settings. - // @todo Should we cache the 'default' view mode by default? Modules won't - // really have a way to alter this because 'default' is not defined in - // hook_entity_view_mode_info(). - if ($view_mode == 'default' || $this->viewModesInfo[$view_mode]['cache']) { + if ((!isset($this->viewModesInfo[$view_mode]) && $this->viewModesInfo['cache_default']) || $this->viewModesInfo[$view_mode]['cache']) { $build[$key]['#cache'] = array( 'keys' => array('entity_view', $this->entityType ,$entity->id(), $view_mode), 'granularity' => DRUPAL_CACHE_PER_ROLE, @@ -201,9 +198,28 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la /** * Implements \Drupal\Core\Entity\EntityRenderControllerInterface::resetCache(). */ - public function resetCache(array $ids = NULL) { - if (isset($ids)) { - cache($this->cacheBin)->deleteTags(array($this->entityType => $ids)); + public function resetCache(array $entities = NULL) { + if (isset($entities)) { + $tags = array(); + foreach ($entities as $entity) { + $tags[$this->entityType][] = $entity->id(); + + // @todo Remove when all entities are converted to EntityNG. + if (!$entity->getPropertyDefinitions()) { + continue; + } + + // Add all the referenced entity types and IDs to the tags that will be + // cleared. + foreach ($entity->getPropertyDefinitions() as $name => $definition) { + if ($definition['type'] == 'entity_reference_field') { + foreach ($entity->$name->getValue() as $target_id) { + $tags[$definition['settings']['target_type']][$target_id] = $target_id; + } + } + } + } + cache($this->cacheBin)->deleteTags($tags); } else { cache($this->cacheBin)->deleteTags(array($this->entityType . '_view' => TRUE)); diff --git a/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php index 25c3709..88e1461 100644 --- a/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityRenderControllerInterface.php @@ -79,9 +79,8 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la /** * Resets the entity render cache. * - * @param array|null $ids - * (optional) If specified, the cache is reset for the entities with the - * given IDs only. + * @param array|null $entities + * (optional) If specified, the cache is reset for the given entities only. */ - public function resetCache(array $ids = NULL); + public function resetCache(array $entities = NULL); } diff --git a/core/modules/block/lib/Drupal/block/BlockRenderController.php b/core/modules/block/lib/Drupal/block/BlockRenderController.php index 2c69b93..0f73347 100644 --- a/core/modules/block/lib/Drupal/block/BlockRenderController.php +++ b/core/modules/block/lib/Drupal/block/BlockRenderController.php @@ -90,7 +90,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la /** * Implements \Drupal\Core\Entity\EntityRenderControllerInterface::resetCache(). */ - public function resetCache(array $ids = NULL) { + public function resetCache(array $entities = NULL) { // @todo Move block render caching logic to this controller? } } diff --git a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php index 7fb8196..41800dc 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php @@ -113,7 +113,7 @@ function testUserSignature() { $this->drupalPost('comment/' . $comment_id . '/edit', $edit, t('Save')); // @todo Temporary cache clear. - drupal_container()->get('plugin.manager.entity')->getRenderController('node')->resetCache(array($node->nid)); + drupal_container()->get('plugin.manager.entity')->getRenderController('node')->resetCache(array($node)); // Assert that the signature did not make it through unfiltered. $this->drupalGet('node/' . $node->nid);