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

146

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
    Commented Aug 10, 2016 at 1:13
  • How can I get parameter, if I visited /taxonomy/term/{tid}?
    – AshwinP
    Commented Mar 27, 2017 at 12:40
  • 1
    Is this a functional replacement for menu_get_object? Commented 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
    Commented Aug 22, 2019 at 8:40
  • 2
    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
    Commented 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. Commented 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
    Commented 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. Commented 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.
    – avpaderno
    Commented 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
    Commented Jun 14, 2023 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 you have read our privacy policy.

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