2

I need add a custom block in my website, this block must show some users values (from customs fields).

I can show all user variables I want but if a user value change for some reason, I must clear all caches for get the news values from the page where is showed my custom block.

I checked the drupal 8 cache API and try find some example, but I don't find how I do for my variables aren't cached. (or cached but refresh when datas user change)

My code is very simple, I have this hook theme (in my .module) :

function main_menu_theme($existing, $type, $theme, $path)
{
  $theme=array();
  $theme['block__main_menu_quick_stat_block'] = array(
    'base hook' => 'block',
    // 'render element' => 'block', // I tried with and without this line..
  );
  return $theme;
}

This is the build method from my Block Class:

class MainMenuQuickStatBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $build = [];

    // Get the currect Drupal User
    $user = _getCurrentUser();

//I tried this...
//  $build['main_menu_quick_stat_block'] = array(
//    '#one' => $user->customfieldone->value,
//    '#two' => $user->customfieldtwo->value,
//  );
//  return $build;

//I tried this...
//    return array(
//      'main_menu_quick_stat_block' => array(
//        '#cache' => array('max-age' => 0),
//        '#one' => $user->customfieldone->value,
//        '#two' => $user->customfieldtwo->value,
//      ),
//    );

//Last try..  
    return array(
        '#one' => $user->customfieldone->value,
        '#two' => $user->customfieldtwo->value,
        '#cache' => [
          'max-age' => 0,
          // 'contexts' => ['user',], // I tried this
          // 'contexts' => 'user', // I tried this
        ],
    );
  }
}

For my custom template, I copied the default block.html.twig template from the theme directory. I can show variables "one" and "two" from the webpage thanks to the custom template. But impossible to configure theses variables for they become uncacheable !

I also try with cache tags but no success..

EDIT 1 (after 4k4 detailed answer !)

This is my last try : From my custom Block Class :

class MainMenuQuickStatBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $build = [];
    $user = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());

    //$user = \Drupal::currentUser(); // If I use this way, I get this error in the "'tags' => $user->getCacheTags()," below : "Error: Call to undefined method Drupal\Core\Session\AccountProxy::getCacheTags() in Drupal\main_menu\Plugin\Block\MainMenuQuickStatBlock->build() (line 52 of modules/custom/main_menu/src/Plugin/Block/MainMenuQuickStatBlock.php)."

    $var1 = $user->field_field1->value; //If a log var1, I get a value here ($var1 = 7 by example)
    $var2 = 42; //its a test

    return [
        '#theme' => 'block__main_menu_quick_stat_block',
        '#test' => 42,
        '#var1' => $var1,
        '#cache' => [
          'contexts' => ['user'],
          'tags' => $user->getCacheTags(),
        ],
    ];
  }
}

From my .module file :

...
function main_menu_theme($existing, $type, $theme, $path)
{
  $theme=array();
  $theme['block__main_menu_quick_stat_block'] = array(
    'variables' => [
      'var1' => NULL,
      'test' => NULL,
    ],
  );

  return $theme;
}

I cleared caches (drush cr)

My custom template block is called when I load the page, but variables 'var1' and 'test' are null. Below, I log variables array with "{{ kint() }}" in my custom template :

enter image description here

I'm going to find drupal guide for know why my vars are null...

2 Answers 2

5

The problem is not your choice of cache parameters. If you set cache max-age to 0 you don't need any other cache setting to disable the cache.

The problem is how you use the block template. You probably render only the single variables you've added and not the default {{ content }} variable. This is the reason the cache info gets lost.

Better use a custom twig template for the custom variables:

hook_theme() in mymodule.module:

function mymodule_theme($existing, $type, $theme, $path) {
  return [
    'mytemplate' => [
      'variables' => [
        'var1' => NULL,
        'var2' => NULL,
      ],
    ],
  ];
}

build() in block class:

  public function build() {

    $user = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
    $var1 = $user->field_field1->value;
    $var2 = $user->field_field2->value;

    return [
      '#theme' => 'mytemplate',
      '#var1' => $var1,
      '#var2' => $var2,
      '#cache' => [
        'contexts' =>  ['user'],
        'tags' => $user->getCacheTags(),
      ],
    ];
  };

mytemplate.html.twig:

<h2>Additional user info</h2>
<p>Field 1: {{ var1 }}</p>
<p>Field 2: {{ var2 }}</p>

The template mytemplate will be nested inside of the block template, which then, if you change it back to default, can merge the cache data to the block level and page elements on higher levels.

4
  • Thanks for your detailed answer :) (+1) I tried your code but I get an issue..I updated my main post Commented Oct 17, 2016 at 18:45
  • 1
    remove block__ from the template name: main_menu_quick_stat_block - about the user, that's a good point in your code, we need to load the full user entity
    – 4uk4
    Commented Oct 17, 2016 at 19:05
  • First, I remove 'block__' from the '#theme' property in the custom Block Class, nothing changed..Then I removed 'block__' from the hook _theme and in my custom template and....Its works ! its marvelous haha :D As you said, the problem was the block 'context' use in my code. Just for curiosity, if I wanted keeps this block 'context', how can I did manage the cache of my block ? It's impossible ? Thanks you again :) Commented Oct 17, 2016 at 19:26
  • 1
    You can implement getCacheTags() and getCacheContexts()in your block plugin. But if you return in build() a render array, what you usually do in a block plugin, you don't need to merge the cache info yourself, this is done by drupal automatically.
    – 4uk4
    Commented Oct 17, 2016 at 20:04
2

What you have should work by disabling cache completely.

'tags' => $user->getCacheTags() would allow you to invalidate, and while you want that too, it still means the block will by default be cached for the first user that accesses it. So what you additionally need is 'contexts'] => ['user'] to indicate that each user needs his own cache.

You could also implement the methods getCacheTags(), getCacheMaxAge() and getCacheContexts() on the block But that shouldn't change anything.

PS: Do not define global functions like _getCurrentUser(), always prefix with a module like main_menu_get_current_user() or make them part of your class (e.g. with a trait if you want to re-use)

1
  • I tried with 'user' context (I added "'contexts' => ['user',],", I updated my first post), but its still not work.. I don't understand why 'max-age' to 0 also doesn't work, I think I missed something somewhere.. but what ? x) the "_getCurrentUser()" function is a quick example for explain I get the current user :) Can you provide me a very simple example where user data is used in a custom block Class and called from a template and with no need clear cache when the user data is updated ? Commented Oct 16, 2016 at 21:53

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.