An easier way to get the current node in a block plugin in Drupal 8


A long time in a galaxy far away… Not really. But it is over a year since I wrote how to get the current node in a block plugin and promised to write a follow-up post with an easier method. Well, better late than never, here it is!

Update: As many pointed out on Twitter, LinkedIn, and in comments here, the context key I use(d) in this article has been deprecated and replaced by a newer context_definitions key. The article is updated to reflect that.

The previous method works, so why bother with another method? In short, it’s less code and less code is easier to maintain. Also, instead of doing all the work to get the node and set the correct cache contexts/tags, it is better to let Drupal handle that for us. In the method I am going to describe today, we just need to tell Drupal that we need a node and Drupal handles everything for us. Sweet.

Context

Let’s jump to the solution. We want to get the node being currently shown from our block plugin, and we will use the “context” (not to be confused with cache contexts) to ask Drupal to send this to us. We do this with our block plugin annotation. Let’s see how.

/**
 * Provides a 'TextAdsBlock' block.
 *
 * @Block(
 *   id = "text_ads",
 *   admin_label = @Translation("Text Ads"),
 *   category = @Translation("Ads"),
 *   context_definitions = {
 *     "node" = @ContextDefinition("entity:node", label = @Translation("Node"))
 *   }
 * )
 */

There is a new context_definitions property set to a ContextDefinition in this annotation which tells Drupal what we need available to us. The "node" key lets us get the node in our code (we’ll see that shortly) and Drupal ensures that this plugin is only available when we have the "entity:node" context available to us.

To get the node in your block plugin, use the getContextValue parent method.

  public function build() {
    $node = $this->getContextValue('node');

    return [
      '#markup' => 'The node id is ' . $node->id(),
    ];
  }

Since we’re using the context here, Drupal already knows how to correctly set the cache contexts and tags. We don’t need to handle that ourselves anymore (you still have to set other cache contexts and tags directly related to your block’s logic).

Complete Code Sample

The complete block plugin may be viewed in this gist. This example is modified from a real-life use case and you can disregard some of the implementation details (such as getting the location and checking for node type). I have included the entire code listing to demonstrate how you might use it in a real scenario where you might have additional constraints such as showing the block only on node pages of certain types.

Takeaways

In Drupal and generally in programming, there are many ways to solve a single problem. The best way is rarely the best in all cases. It is up to you and your use-case to see which method would be most appropriate for your requirements. To that, I invite you to read the first post again and compare the implementation.

Regardless, there are general principles that typically lead to better results. And generally, less code is always better.