69

In Drupal 7, if I wanted to get the node id of the currently displayed node (e.g. node/145) I could get it with the arg() function. In this case, arg(1) would return 145.

How can I achieve the same in Drupal 8?

6 Answers 6

145

The parameter will have been upcasted from nid to full node object by the time you get access to it, so:

$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof \Drupal\node\NodeInterface) {
  // You can get nid and anything else you need from the node object.
  $nid = $node->id();
}

See the change record for more information.

9
  • 12
    I just want to add you have to careful with this - I just got stung where \Drupal::routeMatch()->getParameter('node'); will return an array of 1 item (the node id) on node revision delete pages, so if you calling a method on what you assume to be an object you will get a fatal error like "Fatal error: Call to a member function getType() on string".
    – Jeff Burnz
    Aug 10, 2016 at 1:13
  • How can I get parameter, if I visited /taxonomy/term/{tid}?
    – AshwinP
    Mar 27, 2017 at 12:40
  • 1
    Is this a functional replacement for menu_get_object? May 2, 2018 at 17:09
  • 1
    @AshwinP The parameter is whatever you write inside {} in your route. For taxonomy terms the route paramater is called taxonomy_term, route definition /taxonomy/term/{taxonomy_term}. Here you can get it like this, \Drupal::routeMatch()->getParameter('taxonomy_term').
    – user72672
    Aug 22, 2019 at 8:40
  • 1
    This code isn't working for unpublished node when the user doesn't have the permission to see it. This code $node = \Drupal::requestStack()->getCurrentRequest()->get('node'); will correctly load the node.
    – tostinni
    Oct 30, 2019 at 10:08
24

It is correct to use \Drupal::routeMatch()->getParameter('node'). If you just need the node ID, you can use \Drupal::routeMatch()->getRawParameter('node').

8

Note on the node preview page, the following doesn't work:

$node = \Drupal::routeMatch()->getParameter('node');
$nid = $node->id();

For the node preview page, you have to load the node this way:

$node = \Drupal::routeMatch()->getParameter('node_preview');
$nid = $node->id();

How to load node object in node preview page?

6

if you are using or creating custom block then you have to follow this code to get current url node id.

// add libraries
use Drupal\Core\Cache\Cache;  

// code to get nid

$node = \Drupal::routeMatch()->getParameter('node');
  $node->id()  // get current node id (current url node id)


// for cache

public function getCacheTags() {
  //With this when your node change your block will rebuild
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
  //if there is node add its cachetag
    return Cache::mergeTags(parent::getCacheTags(), array('node:' . $node->id()));
  } else {
    //Return default tags instead.
    return parent::getCacheTags();
  }
}

public function getCacheContexts() {
  //if you depends on \Drupal::routeMatch()
  //you must set context of this block with 'route' context tag.
  //Every new route this block will rebuild
  return Cache::mergeContexts(parent::getCacheContexts(), array('route'));
}
1
  • This answer is 100% incorrect. Do not do this. Dec 25, 2022 at 5:26
1

By using Service,

    use Drupal\Core\Routing\RouteMatchInterface;

/**
 * Provides a 'UtilitiesDataService' service.
 */
class MyClass {
 

  /**
   * The route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Constructs a new service.
   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match.
   */
  public function __construct(RouteMatchInterface $route_match) {

    $this->routeMatch = $route_match;
  }

  public function getNodeId(){
    $nid = $this->routeMatch->getParameter('node')->id();
  }
}

In services.yml file,

services:
   my_module.get_data_service:
    class: Drupal\my_module\MyClass
    arguments: ['@current_route_match']
4
  • This is not "by dependency injection" - this is the creation of a new service. You're going to write a new service just to wrap one method of the existing current_route_match service? This is just wrong. Use @Clive's answer, which accesses the current_route_match service directly.
    – anonymous
    Oct 1, 2021 at 20:35
  • 1
    I think it is correct. A similar example is also in change record link Clive has provided. The example Sneha has provided is better than a static call others have provided. Jul 5, 2022 at 10:01
  • 1
    Procedural code would still need to make a static call to \Drupal. In this case, using the service defined in this answer or the current_route_match service doesn't make any difference, which means the static call isn't removed.
    – apaderno
    Dec 25, 2022 at 9:44
  • 1
    Agree with @apade. This isn't providing any benefit over making the call to the CurrentRouteMatch class.
    – welly
    Jun 14 at 13:21
1

Blocks are context aware and you should be using them. This will allow using the blocks in places where the route itself doesn't have a node but some subsystem provides the context anyways.

Example from core:

/**
 * Provides a 'Node Context Test' block.
 *
 * @Block(
 *   id = "node_block_test_context",
 *   label = @Translation("Node Context Test"),
 *   context_definitions = {
 *     "node" = @ContextDefinition("entity:node", label = @Translation("Node"))
 *   }
 * )
 */
class NodeContextTestBlock extends BlockBase {

  public function build() {
    /** @var \Drupal\node\NodeInterface $node */
    $node = $this->getContextValue('node');

You can also see how easy it is to get the node. The test this block is used for is instructional: it tests whether the block appears on the preview page. This is not doable with the route match approach as the /node/preview/{node_preview}/{view_mode_id} route doesn't contain a node at all.

This will also make the node context required so there's no need to check whether there is a node at all. It's possible to make the node optional, of course:

 *     "node" = @ContextDefinition("entity:node", label = @Translation("Node"), required = FALSE)

the order of options after the first argument does not matter.

Your Answer

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

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