5

Short version:

I am using a configuration override to override the site name. Changes are reflected in the HTML title, but not in the system branding block. Drupal keeps serving this block from the render cache.

Long version:

I am using a configuration override service that implements ConfigFactoryOverrideInterface, which overrides configs on demand. Slightly simplified, it looks like this:

class MyConfigOverride implements ConfigFactoryOverrideInterface {

  /**
   * {@inheritdoc}
   */
  public function loadOverrides($names) {
    $overrides = [];

    if ($override_required) {
      if (in_array('system.site', $names)) {
        $overrides['system.site']['name'] = 'My name';
      }
    }
    return $overrides;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheSuffix() {
    return 'MyConfigOverride';
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata($name) {
    $metadata = new CacheableMetadata();

    if ($name === 'system.site') {
      $my_entity = MyEntity::Load(1);
      if ($override_required && !empty($my_entity)) {
        // Append entity cache metadata (cache tags and context).
        $metadata->addCacheableDependency($my_entity);
      }
    }
    return $metadata;
  }

  /**
   * {@inheritdoc}
   */
  public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
    return NULL;
  }

}

However, as soon as this override kicks in, the render cache for the System branding block does not get updated with the new site name. This is because the SystemBrandingBlock class does implement getCacheTags() in which it merges the system.site cache tags (see below), but it does not implement getCacheContexts() (which my module uses since I serve a variation of the page).

/**
 * {@inheritdoc}
 */
public function getCacheTags() {
  return Cache::mergeTags(
    parent::getCacheTags(),
    $this->configFactory->get('system.site')->getCacheTags()
  );

}

I have tried to overcome this by adding additional 'empty' cache metadata to my class for the cases where the override does not apply.

  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata($name) {
    $metadata = new CacheableMetadata();

    if ($name === 'system.site') {
      $my_entity = MyEntity::Load(1);
      if ($override_required && !empty($my_entity)) {
        // Append entity cache metadata.
        $metadata->addCacheableDependency($my_entity);
      }
      else {
        // Append 'empty' cache metadata.
        $metadata->addCacheContexts(['mycachecontext']);
        $metadata->addCacheTags(['myentity', 'myentity:0']);
      }
    }
    return $metadata;
  }

Since the block does not inherit the cache contexts of system.site and I do not update or otherwise invalidate any of the provided cache tags ('myentity', 'myentity:0'), the render cache still persists. I can append cache context to the system branding block in a hook_block_build_BASE_BLOCK_ID_alter implementation, but I'd prefer not to implement such specific exceptions as they will never cover all situations.

I am somewhat hoping for a hook to update all renderable elements (blocks?) and append cache contexts to those that inherit cache tags provided by system.site. But I am open to alternative solutions as well.

How can I make any block or other entity in the render cache, that is depending on the system.site config aware of the config override?

2

In the meantime I have come up with this approach, but I am still open to other solutions.

I have found a solution in a pre_render callback for the blocks, by deconstruction the cacheable metadata, searching it for a system.site tag and append my own context and tags (if required).

/**
 * Implements hook_block_view_alter().
 */
functionmymodule_block_view_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) {
  $build['#pre_render'][] = 'mymodule_block_view_pre_render';
}

/**
 * #pre_render callback: Sets my module cache tags.
 */
function mymodule_block_view_pre_render(array $build) {
  $cacheable_metadata = CacheableMetadata::createFromRenderArray($build);
  $tags = $cacheable_metadata->getCacheTags();

  if (in_array('config:system.site', $tags)) {
    // Always add a cache context, so we have a variant without overrides as well.
    $cacheable_metadata->addCacheContexts(['mycontext']);

    if ($entity = MyEntity::Load(1)) {
      $cacheable_metadata->addCacheableDependency($entity );
    }
  }

  $cacheable_metadata->applyTo($build);

  return $build;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.