Skip to content
This repository has been archived by the owner on Aug 18, 2024. It is now read-only.

Commit

Permalink
Do not invalidate group content list cache tags when the group itself…
Browse files Browse the repository at this point in the history
… changes (#509)

Do not invalidate group content list cache tags when the group itself changes
  • Loading branch information
pfrenssen authored May 17, 2019
2 parents f004716 + 5272058 commit 380756a
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 26 deletions.
8 changes: 3 additions & 5 deletions og.module
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,10 @@ function og_theme($existing, $type, $theme, $path) {
function og_invalidate_group_content_cache_tags(EntityInterface $entity) {
// If group content is created or updated, invalidate the group content cache
// tags for each of the groups this group content belongs to. This allows
// group listings to be cached effectively. Also invalidate the tags if the
// group itself changes. The cache tag format is
// group listings to be cached effectively. The cache tag format is
// 'og-group-content:{group entity type}:{group entity id}'.
$is_group_content = Og::isGroupContent($entity->getEntityTypeId(), $entity->bundle());
$group_is_updated = Og::isGroup($entity->getEntityTypeId(), $entity->bundle()) && !empty($entity->original);
if ($group_is_updated || $is_group_content) {
if ($is_group_content) {
/** @var \Drupal\og\MembershipManagerInterface $membership_manager */
$membership_manager = \Drupal::service('og.membership_manager');
$tags = [];
Expand All @@ -345,7 +343,7 @@ function og_invalidate_group_content_cache_tags(EntityInterface $entity) {
// the tags of the old group(s).
/** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
$original = !empty($entity->original) ? $entity->original : NULL;
if ($is_group_content && $original) {
if ($original) {
/** @var \Drupal\og\OgGroupAudienceHelperInterface $group_audience_helper */
$group_audience_helper = \Drupal::service('og.group_audience_helper');
/** @var \Drupal\Core\Entity\FieldableEntityInterface $original */
Expand Down
141 changes: 120 additions & 21 deletions tests/src/Kernel/Entity/CacheInvalidationOnGroupChangeTest.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
<?php

declare(strict_types = 1);

namespace Drupal\Tests\og\Kernel\Entity;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\KernelTests\KernelTestBase;
use Drupal\og\Og;
use Drupal\og\OgGroupAudienceHelperInterface;

/**
* Tests if the cache is correctly invalidated on group change.
* Tests if group content listings are invalidated when group audience changes.
*
* @group og
*/
class CacheInvalidationOnGroupChangeTest extends KernelTestBase {

/**
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;

/**
* {@inheritdoc}
*/
Expand All @@ -35,6 +45,8 @@ protected function setUp() {
$this->installEntitySchema('entity_test');
$this->installEntitySchema('user');

$this->cache = $this->container->get('cache.default');

// Add a OG group audience.
Og::groupTypeManager()->addGroup('entity_test', 'group');
$settings = [
Expand All @@ -61,39 +73,126 @@ protected function setUp() {
*/
public function testCacheInvalidationOnGroupChange() {
// Create two groups.
$group1 = EntityTest::create([
'type' => 'group',
'name' => $this->randomString(),
]);
$group1->save();
$group2 = EntityTest::create([
'type' => 'group',
'name' => $this->randomString(),
]);
$group2->save();
$groups = [];
for ($i = 0; $i < 2; $i++) {
$groups[$i] = EntityTest::create([
'type' => 'group',
'name' => $this->randomString(),
]);
$groups[$i]->save();
}

// Create a group content.
// Create a group content entity that belong to the first group.
$group_content = EntityTest::create([
'type' => 'group_content',
'name' => $this->randomString(),
OgGroupAudienceHelperInterface::DEFAULT_FIELD => $group1->id(),
OgGroupAudienceHelperInterface::DEFAULT_FIELD => $groups[0]->id(),
]);
$group_content->save();

// Cache some arbitrary data tagged with the OG group content tag.
$bin = \Drupal::cache();
$cid = strtolower($this->randomMachineName());
$tags = Cache::buildTags('og-group-content', $group1->getCacheTagsToInvalidate());
$bin->set($cid, $this->randomString(), Cache::PERMANENT, $tags);
// Cache some arbitrary data tagged with the OG group content tags for both
// groups.
$this->populateCache($groups[0]);
$this->populateCache($groups[1]);

// Sanity check, the cached content listings of both groups should be
// populated.
$this->assertCachePopulated($groups[0]);
$this->assertCachePopulated($groups[1]);

// Change the label of group 1. This should not affect any of the cached
// listings.
$groups[0]->setName($this->randomString())->save();
$this->assertCachePopulated($groups[0]);
$this->assertCachePopulated($groups[1]);

// Move the group content from group 1 to group 2. This should invalidate
// the group content list cache tags of both groups.
$group_content
->set(OgGroupAudienceHelperInterface::DEFAULT_FIELD, $group2->id())
->set(OgGroupAudienceHelperInterface::DEFAULT_FIELD, $groups[1]->id())
->save();

// Cache entries tagged with 'og-group-content:entity_type:{$group->id()}'
// should have been invalidated at this point because the content members of
// $group1 have changed.
$this->assertFalse($bin->get($cid));
// both groups have changed.
$this->assertCacheNotPopulated($groups[0]);
$this->assertCacheNotPopulated($groups[1]);

// Now populate both caches while including the cache tags of the group
// itself. This can happen for example if a listing of group content is
// shown that includes the group name in its content.
$this->populateCache($groups[0], TRUE);
$this->populateCache($groups[1], TRUE);

// Change the label of group 1. This should invalidate the cache of the
// group content listing for group 1, but not for group 2.
$groups[0]->setName($this->randomString())->save();
$this->assertCacheNotPopulated($groups[0]);
$this->assertCachePopulated($groups[1]);
}

/**
* Caches a listing of group content that belongs to the given group.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $group
* The group for which to cache a group content listing.
* @param bool $include_group_cache_tag
* Whether or not the group content listing is tagged with the group's cache
* tags.
*/
protected function populateCache(ContentEntityInterface $group, bool $include_group_cache_tag = FALSE): void {
$cid = $this->generateCid($group);
$tags = Cache::buildTags('og-group-content', $group->getCacheTagsToInvalidate());
if ($include_group_cache_tag) {
$tags = Cache::mergeTags($tags, $group->getCacheTagsToInvalidate());
}
$this->cache->set($cid, $this->randomString(), Cache::PERMANENT, $tags);
}

/**
* Generates a cache ID for a group content listing of the given group.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $group
* The group for which to generate a group content listing cache ID.
*
* @return string
* The cache ID.
*/
protected function generateCid(ContentEntityInterface $group): string {
return implode(':', ['my_group_content_listing', $group->id()]);
}

/**
* Checks if the group content listing cache for a given group is populated.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $group
* The group for which to perform the check.
*/
protected function assertCachePopulated(ContentEntityInterface $group): void {
$this->assertTrue($this->getCachedData($group));
}

/**
* Checks if the group content listing cache for a given group is unpopulated.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $group
* The group for which to perform the check.
*/
protected function assertCacheNotPopulated(ContentEntityInterface $group): void {
$this->assertFalse($this->getCachedData($group));
}

/**
* Returns the cached group content listing for a given group, if available.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $group
* The group for which to return the cached group content listing.
*
* @return false|object
* The cache item or FALSE on failure.
*/
protected function getCachedData(ContentEntityInterface $group) {
return $this->cache->get($this->generateCid($group));
}

}

0 comments on commit 380756a

Please sign in to comment.