2

I have a simple custom block with a user cache context and a specific user specific cache tag:

/**
 * Provides a 'User example' block.
 *
 * @Block(
 *   id = "user_example_block",
 *   admin_label = @Translation("User example block"),
 *   category = @Translation("Blocks")
 * )
 */
class UserExampleBlock extends BlockBase implements BlockPluginInterface {

  /**
   * {@inheritdoc}
   */
  public function build() {
    return [
      '#markup' => '<span>Current User ID: ' . \Drupal::currentUser()->id() . '</span>',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts()  {
    return Cache::mergeContexts(parent::getCacheContexts(), ['user']);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    return Cache::mergeTags(parent::getCacheTags(), ['example_user:' . \Drupal::currentUser()->id()]);
  }
}

The block output varies correctly by the logged in user but the user specific cache tag is not set correctly.

It seems the cache tag is only set once for users with the same role or permissions.

Here is how to reproduce it:

  1. Rebuild cache (drush cr)
  2. Login with User 100 -> The block output is correct. The correct cache tag is set (example_user:100).
  3. Logout
  4. Login with User 101 (same role as User 100) -> The output of the block is correct, but the cache tag was not changed, it still is example_user:100. In fact the getCacheTags, getCacheContexts methods were not even called.

What works is when I put the cacheability meta data in the build method (with the "#cache" element in the render array).

What could be the reason for this?

Thanks for any help.

8
  • 1
    It shouldn't make a difference, the result of getCacheContexts/getCacheTags gets merged into the build array anyway. The only reason for this different behavior could be that you render only parts of the block content in a way that you get the #cache element you've placed yourself but not the automatically merged cache data of the block plugin. Check $build in a hook_block_view_alter() to see where the different #cache data ends up.
    – 4uk4
    Commented Dec 2, 2019 at 14:42
  • @4x4 I think you mean the hook_block_build_alter()? In hook_block_view_alter() there is no $build['#cache#'] element. The #cache element in hook_block_build_alter() seems to be correct for the first user, all keys, the context and cache tags are correctly merged. But when I logout and login with the second user, the hook_block_build_alter() hook is not called again on a page that was visited by the first user before. Could the reason for this be the "auto_placeholder_conditions" config? The user context is set here to render deferred. Commented Dec 3, 2019 at 7:11
  • @4k4 Additionally I do not render only parts of the block, its a simple block configured as the first block in the "content" area on the "Block layout" page without any change to the default config. Commented Dec 3, 2019 at 7:12
  • @4k4 I just removed the "user" context from the "auto_placeholder_conditions" config and the caching behavior now works as expected. It seems that the getCacheTags and getCacheContexts methods of the block are not called when the block has a "auto placeholder context". Is this a bug or the expected behavior? Then this SHOULD be documented. Commented Dec 3, 2019 at 7:20
  • Placeholdering would be a good thing, then the block is cached as placeholder, which gets replaced at the end of each request for the current user. But this doesn't involve the entire block build, placeholders are replaced by only rendering the inner part in a simple callable. I can't debug this from afar, but I doubt there is a core issue which would prevent the cache context from bubbling up either way.
    – 4uk4
    Commented Dec 3, 2019 at 7:23

1 Answer 1

2

To sum up the comments: As a general rule, if you render the current user, then always add the correct cache metadata to the render element:

 public function build() {
    return [
      '#markup' => '<span>Current User ID: ' . \Drupal::currentUser()->id() . '</span>',
      '#cache' => [
        'contexts' => ['user'],
        'tags' => ['user:' . \Drupal::currentUser()->id()],
      ],
    ];
  }

If you have a block, which doesn't return a cacheable render result in all cases, you can add additional cache metadata in the block plugins cacheable dependencies methods. It doesn't harm when you declare a cache tag or context twice.

2
  • Thank you very much! :) Commented Dec 3, 2019 at 8:05
  • Thank you for reporting the problem. Made me rethink an old question I was never happy how it was discussed and so I made my point back then more clearly, see drupal.stackexchange.com/a/199533/47547
    – 4uk4
    Commented Dec 3, 2019 at 9:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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