0

I implement a menu on a region using a preprocess function to get a link with an anchor for each block of it with a title. I made a preprocess function for region, in my theme, where I test the region machine name.

I first used the $variables['elements'][XXX]['#id'] to load each block, to find out if the label was set to be display. But I quickly discover that on my server, where the cache is on, the $variables['elements'] only contain an array of cached render.

I then modify my preprocess hook to use the key of the $variables['elements'], witch is finally the block key.

use Drupal\block\Entity\Block;

/**
 * Implements hook_preprocess_region().
 */
function MYTHEME_preprocess_region(&$variables) {
  if ($variables['region'] == 'one_page') {
    $blocs_menu = array();
    foreach ($variables['elements'] as $key => $values) {
      if (substr($key, 0, 1) == '#') {
        continue;
      }
      $bloc_object = Block::load($key);
      if (is_object($bloc_object)) {
        $bloc_settings = $bloc_object->get('settings');
        if ($bloc_settings['label_display']) {
          $blocs_menu[] = array(
              'id' => $key,
              'label' => $bloc_settings['label'],
          );
        }
      }
    }
    if (count($blocs_menu)) {
      $variables['region_menu'] = $blocs_menu;
    }
  }
}

And in my override file region--one-page.html.twig

{%- if region_menu -%}
<nav class="page-nav-anchor">
  <ul>
{%- for menu in region_menu -%}
  <li><a rel="scroll" href="#block-{{ menu['id'] }}">{{ menu['label'] }}</a></li>
{%- endfor -%}
  </ul>
</nav>
{%- endif -%}

Everything is working well, but I would like to improve the concept :

  • Be able to detect if the cache is activated on the project
  • Include in cache my 'region_menu' variable, and generate it only when cache have to be rebuild.

If I understand well, the twig template is also cached, then it is maybe just not really necessary to declare the menu if cache is ON ?

The thing is I'm really lost in the D8 cache API. I think I have to deal with the cache tags, because I don't have a render to cache.

I would appreciate some help, I cannot find some good (and easy) examples on the documentation or in the core modules.

0

Regions are not cached on their own. What you build in a region template will be part of cached pages. If most pages you deliver are cached, this might be OK.

To optimize it build the menu in a block plugin and place the block at the top of the region. The block plugin can get the blocks from BlockRepository::getVisibleBlocksPerRegion. Use the access cache metadata this method provides for the region, add cache tags for all block entities and the tag config:block_list, so that the menu block gets invalidated when the block layout is modified.

  • Thanks for your answer, my idea is to have this functionality directly in the theme and not to develop a module for this. However, even if regions do not have cache of their own, is this not possible to use custom cache part for this, as in D7 using cache_set ? – TytooF Jul 23 '17 at 13:51
  • This new menu block is perfect for caching. We know all dependencies and it will stay in the cache as long as nothing changes in the block layout. The region on the other hand contains multiple blocks which may contain more or less dynamic data and thus is far less cacheable than the menu block. – 4k4 Jul 24 '17 at 11:46
  • Sorry I didn't had time before. Do you think it is possible to define such a block directly in the theme ? I really don't want to have a custom module depending on a custom theme, and would prefer to have this behaviour included in the theme. – TytooF Aug 15 '17 at 11:39
  • 1
    The region is not the optimal place for providing the cache tags because there can be too much dynamic data in it, like I've explained. But you can leave it as it is (uncached) and rely on page caching, which should cover a lot of case scenarios. When you see that you need to optimize I would recommend a module with a block plugin. – 4k4 Aug 15 '17 at 12:48
  • I test to create a block module to replace the preprocess_region and the specific template. I use as proposed the getVisibleBlocksPerRegion method, witch list all block of all regions. But I'm now stuck because I cannot know in witch region the block is actually published. If on some page their is two menu blocks in two regions, I cannot tell one from another and show on each block labels of its region. An idea how to get the current region of the block ? What I read tell me it is impossible because it is the entity who knows it... Thanks for your help – TytooF Aug 21 '17 at 21:57

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.