4

A very basic 8.x API question.

What is the approved/simplest methods of confirming a node matching a certain content type and ID exists, and then loading it as an object?

(Use case: a module which accepts a node ID in the URL, that needs to be checked.)

Currently I have:

$node = \Drupal::entityTypeManager()->getStorage('node')->load(123);

if ($node->getType() == 'application') {
  return $node;
} else {
  return false;
}

My first attempt was in reserve (longer/less efficient):

$nids = \Drupal::entityQuery('node')
  ->condition('type', 'application')
  ->condition('nid', 123)
  ->execute();

// followed by entityTypeManager()->getStorage('node') etc.

But I was wondering if there was a way to make entityQueue() return a flat array of IDs rather than an associative [1=>1,2=>2,3=>3] which you then have to mess around with.

1
  • Is this always a node argument? You could upcast the argument in your route to node and have the object available in the Request, without having to do an entityQuery.
    – Kevin
    May 24, 2018 at 13:22

4 Answers 4

6

I don't know how your module URL/API is structured, but if your module route has something like myapi/foo/bar/(id) to return a node object, you could upcast it instead. Here is an example from the node module:

entity.node.version_history:
  path: '/node/{node}/revisions'
  defaults:
    _title: 'Revisions'
    _controller: '\Drupal\node\Controller\NodeController::revisionOverview'
  requirements:
    node: \d+

{node} will be upcast to a node object and available in the incoming request to the receiving class.

See: https://www.drupal.org/docs/8/api/routing-system/using-parameters-in-routes

So when you pass that to the receiving method:

use Drupal\node\NodeInterface;

class ExampleController {
  public function foo(NodeInterface $node) {
    if ($node->getType() != 'application') {
      // do something, throw Exception, etc?
    } 

    // Otherwise this node is an application type.
    return $node;
  }
}

There may be a way to expand on this in the route definition, so you can upcast the node and ensure it is a specific type before doing anything else, eliminating the checking in the method - but I am not sure.

Perhaps:

2
  • 1
    thanks, excellent answer. I've got that working now, and with a _form rather than a generic controller. (Crucial bit, for others reading, is the parameter must be called 'node' in the route, so it works with NodeInterface.) From reading docs, I'm not sure you can check the type in the routing without writing your own paramconverter... May 24, 2018 at 14:35
  • 1
    @WilliamTurrell You're right, you'd need to write your own parameter converter to also check the type. It's trivial though, only a few lines of code
    – Clive
    May 24, 2018 at 14:44
2

What is the approved/simplest methods of confirming a node matching a certain content type and ID exists, and then loading it as an object?

I could easily be wrong, but I'm not sure there is one, really. It depends how you define "simplest".

Personally I'd test the object type first:

$node = \Drupal::entityTypeManager()->getStorage('node')->load(123);
if ($node instanceof \Drupal\node\Entity\Node && $node->getType() == 'application') {

}

If you're just talking about performance between the two different approaches, then yes, the first approach is better as it requires at most one query, whereas the second requires two.

rather than an associative [1=>1,2=>2,3=>3] which you then have to mess around with.

I'm not sure what you mean by that; an associative array is exactly the same as a "flat" array in that example - just ignore the keys or values as required.

2

I'd do this:

$nid = 123;
$type = 'application';
$node_exists = (bool) db_query('SELECT 1 FROM {node} WHERE nid = :nid AND type = :type', [':nid' => $nid, ':type' => $type)->fetchField();

if ($node_exists) {
  return node_load($nid);
}

return FALSE;
2

I'm using EntityStorageBase::loadByProperties, because it's flexible and lightweight in my eyes. It returns an array, so you can use reset() to get the node if exists:

$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([          
  'nid' => $nid,
  'type' => 'application'
]);
if ($node = reset($nodes)) {
  // do something
}
2
  • 1
    Combine this with a custom param converter and I think we have a winner...
    – Clive
    May 24, 2018 at 15:39
  • 1
    Actually, this internally does an entity query and loading the node. Just doing a load() is clearly more efficient here, especially of the node is already in the persistent or even static cache
    – Berdir
    May 24, 2018 at 19:37

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.