0

I'm getting a crash on node save when my node contains a custom token. I'm using the Token Filter module to display tokens in node content.

To make the tokens I followed this tutorial, which is similar to the Token documentation.

Now, with Drupal 8.8, when I save a new node that contains one of my custom tokens, I get a WSOD with this error:

Drupal\Core\Entity\EntityStorageException: The "node" entity cannot have a URI as it does not have an ID in Drupal\Core\Entity\Sql\SqlContentEntityStorage->save() (line 846 of core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).

But the real error appears to be from token:

Drupal\Core\Utility\Token->generate('ebook', Array, Array, Array, Object) (Line: 196)
Drupal\Core\Utility\Token->replace('
Test [ebook:custom-type] 2

', Array, Array, Object) (Line: 130)
Drupal\token_filter\Plugin\Filter\TokenFilter->process('
Test [ebook:custom-type] 2

', 'ja') (Line: 118)
Drupal\filter\Element\ProcessedText::preRenderText(Array)
call_user_func_array(Array, Array) (Line: 100)
Drupal\Core\Render\Renderer->doTrustedCallback(Array, Array, 'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725', 'silenced_deprecation', 'Drupal\Core\Render\Element\RenderCallbackInterface') (Line: 781)
Drupal\Core\Render\Renderer->doCallback('#pre_render', Array, Array) (Line: 372)

Weirdly, if I remove the token from the body field when I save the node, the node saves properly-- and then I can edit the node and add the token back in without any error. The error only occurs when the node is saved and it has a token in it.

Drupal tokens like [site:name] work fine; it's just my custom token that causes an error.

Here's the relevant code:

/**
 * Implements hook_token_info().
 */
function MYMODULE_token_info() {

  $ebook_type = [
    'name' => t('Custom Tokens'),
    'description' => t('Tokens to make upgrading to D9 harder.'),
  ];

  $ebook['custom-type'] = [
    'name' => t("Custom type"),
    'description' => t('Apples or oranges'),
  ];

  return [
    'types' => ['ebook' => $ebook_type],
    'tokens' => ['ebook' => $ebook],
  ];

}

/**
 * Implements hook_tokens().
 */
function MYMODULE_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {

  $replacements = [];

  if ($type == 'ebook' && !empty($data['node'])) {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'custom-type':
          $replacements[$original] = MYMODULE_token_set_custom_type($data['node']);
          break;
      }
    }
  return $replacements;
}

function MYMODULE_token_set_custom_type($node) {
  $alias = $node->toURL()->toString();
  preg_match('(/.*/)', $alias, $matches);
  if ($matches[0] == '/apples/') {
    return "Apples";
  }
  else {
    return "Oranges";
  }
}

How do I resolve this?

1
  • 2
    This might be connected to the presave hook from an earlier question where you apply text filters before the node is saved.
    – 4uk4
    May 26, 2020 at 13:20

2 Answers 2

1

If the token is being generated before the node has been saved, this will cause a WSOD, since no URI can be created for the node. Entity URIs require an entity ID to built the path to the entity.

As such, I'd change your code to:

function MYMODULE_token_set_custom_type($node) {
  if ($alias = $node->toURL()->toString()) {
    preg_match('(/.*/)', $alias, $matches);
    if ($matches[0] == '/apples/') {
      return "Apples";
    }
    else {
      return "Oranges";
    }
  }
}
1
  • As a side note, I'm guessing you may want/need to change your regex to '(/.*?/)' to be more specific with your results, but that's really off topic for this issue.
    – Jaypan
    May 26, 2020 at 14:31
1

Further debugging and the comment from 4k4 and the answer from Jaypan provided the hints I needed to solve this.

First, although this was caused by the token, it was also caused by the following call in hook_node_presave():

function MYMODULE_node_presave(NodeInterface $entity) {

      $processed_display = \Drupal::service('renderer')->renderPlain($myspecialviewmode_render);
      $entity->set('field_text_main_display', $processed_display);
      $entity->field_text_main_display->format = 'processed';

It wasn't the token or this hook by themselves that was causing the issue, but their presence together.

To fix this, I rewrote my token to not rely on the URL at all.

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.