Change record status: 
Project: 
Introduced in branch: 
8.0.x
Introduced in version: 
8.0-beta13
Description: 

In versions of Drupal core prior to 8.0.0-beta13 block plugins that utilized context-aware condition plugins in order to determine their visibility had the context values set by the BlockPageVariant dispatching a series of BlockEvents. These events were subscribed to by services that were able to provide global contexts.

This event was being dispatched for each block, even if not needed and in most cases the context providing event subscribers were calculating a global context multiple times.

In order to improve the performance, and provide a reusable architecture for contrib projects requiring global context, the BlockEvents were removed in favour of a LazyContextRepository.

Now only required contexts are loaded, and only once.

API changes | Changes needed by contrib module authors

\Drupal\block\Event\BlockEvents, \Drupal\block\Event\BlockContextEvent and \Drupal\block\EventSubscriber\BlockSubscriberBase are removed
Context providing services must be tagged as context_provider and implement Drupal\Core\Plugin\Context\ContextProviderInterface
\Drupal\block\BlockRepositoryInterface changes thus:

-  public function getVisibleBlocksPerRegion(array $contexts, array &$cacheable_metadata = []);
+  public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []);

The following context providing services were moved out of block module into their respective modules
Drupal\block\EventSubscriber\CurrentLanguageContext » Drupal\Core\Language\ContextProvider\CurrentLanguageContext
Drupal\block\EventSubscriber\NodeRouteContext » Drupal\node\ContextProvider\NodeRouteContext
Drupal\block\EventSubscriber\CurrentUserContext » Drupal\user\ContextProvider\CurrentUserContext

hook_update_N() for contrib modules

On top of changing the code of block context providers, modules who provide them should also write update functions in order to update existing blocks.

The following steps are needed:

  • Write a hook_update_N() function which converts your context IDs to the new one:
    function mymodule_update_8001() {
      $old_context_id = 'mymodule_context';
      $new_context_service_id = '@mymodule.context_provider:mymodule_context';
    
      $config_factory = \Drupal::configFactory();
    
      $backup_values = \Drupal::keyValue('update_backup')->get('block_update_8001');
      foreach ($backup_values as $block_id => $backup_value) {
        $missing_context_ids = $backup_value['missing_context_ids'];
        $status = $backup_value['status'];
        if (!empty($missing_context_ids[$old_context_id])) {
          $condition_plugin_ids = $missing_context_ids[$old_context_id];
          $block = $config_factory->getEditable('block.block.' . $block_id);
          $visibility = $block->get('visibility');
          foreach ($condition_plugin_ids as $condition_plugin_id) {
            $visibility[$condition_plugin_id]['context_mapping'][$old_context_id] = $new_context_service_id;
          }
    
          // Cleanup the remaining missing context IDs.
          $pos = array_search($old_context_id, $missing_context_ids);
          unset($missing_context_ids[$pos]);
          if (empty($missing_context_ids)) {
            // No backup values left, remove the key completely so that block_update_8002() does not disable the block.
            unset($backup_values[$block_id]);
          }
          $block->save();
        }
      }
      \Drupal::keyValue('update_backup')->set('block_update_8001', $backup_values);
    }
    
  • Ensure that your hook_update_N() function runs after block_update_8001 and before block_update_8002 using hook_update_dependencies()
    hook_update_dependencies() {
      $dependencies['mymodule'][8001] = [
        'block' => 8001,
      ];
      $dependencies['block'][8002] = [
        'mymodule' => 8001,
      ];
    
    }
    

Data model changes

Visibility conditions now store a reference to the service ID as well as the context slot name in their mapping.

Before

visibility:
  node_type:
    id: node_type
    bundles:
      article: article
    negate: false
    context_mapping:
      node: node.node

After

visibility:
  node_type:
    id: node_type
    bundles:
      article: article
    negate: false
    context_mapping:
      node: @node.node_route_context:node
Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done