Custom tokens breaking on node save with “node entity cannot have a URI” error

drupal.stackexchange https://drupal.stackexchange.com/questions/293981

  •  25-02-2021
  •  | 
  •  

문제

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?

도움이 되었습니까?

해결책

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";
    }
  }
}

다른 팁

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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 drupal.stackexchange
scroll top