Рейтинг:0

$node->save() не работает при запуске в post_update

флаг us

У меня есть простая функция hook_post_update, которая завершается со следующей ошибкой:

[уведомление] Начато обновление: ssc_custom_post_update_9001

[ошибка] LogicException: Контекст рендеринга пуст, потому что render() был вызван вне вызова renderRoot() или renderPlain(). Использовать вместо этого renderPlain()/renderRoot() или #lazy_builder/#pre_render. в Drupal\Core\Render\Renderer->doRender() (строка 244 E:\www\myssc\html\core\lib\Drupal\Core\Render\Renderer.php). [ошибка] Контекст рендеринга пуст, потому что render() был вызван вне вызова renderRoot() или renderPlain(). Использовать вместо этого renderPlain()/renderRoot() или #lazy_builder/#pre_render. [ошибка] Ошибка обновления: ssc_custom_post_update_9001

В строке ProcessBase.php 171:

Невозможно декодировать вывод в JSON: синтаксическая ошибка

[ОШИБКА] [узел] [2022-03-12T04:58:35] LogicException: визуализация контекста пуст, потому что функция render() была вызвана вне функции renderRoot() или Вызов рендерПлайн(). Используйте renderPlain()/renderRoot() или Вместо этого #lazy_builder/#pre_re nder. в Drupal\Core\Render\Renderer->doRender() (строка 244 E:\www\myssc\html\core\lib\Drupal\Core\Render\Renderer.php). | ИД: 0 | запрос-uri: http://default/ | см.: | IP-адрес: 127.0.0.1 | соединять:
{ "0": { "ssc_custom": { "9001": { "#прервать": { "успех": ложь, "query": "Drupal\Core\Entity\EntityStorageException: Контекст рендеринга пуст, потому что render() был вызван вне вызов renderRoot() или renderPlain(). Используйте renderPlain()/renderRoot() или #lazy_builder/#pre_render. в друпал\кор\энт
ity\Sql\SqlContentEntityStorage->save() (строка 810 E:\www\myssc\html\core\lib\Drupal\Core\Entity\Sql\SqlContentEntityStorage.php)." } } }, "#прервать": [ "ssc_custom_post_update_9001" ] }, "drush_batch_process_finished": правда }

Я сократил код до минимума:

используйте Drupal\Core\Entity\EntityStorageInterface;
используйте Drupal\Core\Entity\EntityTypeManagerInterface;
используйте Drupal\node\NodeInterface;

функция ssc_custom_post_update_9001(&$песочница) {
  /** @var \Drupal\node\NodeStorageInterface $node_storage */
  $node_storage = \Drupal::entityTypeManager()->getStorage('узел');

  // Сколько объектов обрабатывать за пакет.
  $лимит = 5;

  $node_ids = $node_storage->getQuery()
    ->Проверка доступа(ЛОЖЬ)
    ->условие('тип', 'статья')
    -> диапазон (0, $ лимит)
    ->выполнить();
  // Загрузить сущности.
  $nodes = $node_storage->loadMultiple($node_ids);

  /** @var \Drupal\node\NodeInterface $node */
  foreach ($nodes как $node) {
    $узел->setNewRevision();
    $узел->сохранить();
  }

  $песочница['#finished'] = 1;

}

Если я запускаю тот же код напрямую (не из drush updb), он работает нормально. Запуск из админ-меню «Выполнить обновления»; он также терпит неудачу (так что это не проблема drush).

Комментируя $node->save(), ошибка не возникает.

sonfd avatar
флаг in
Это происходит, если вы загружаете и сохраняете контент другого типа?
leymannx avatar
флаг ne
Это не то, как работает партия. Обновления контента должны происходить в `hook_deploy_N`, запущенном после импорта конфигурации при выполнении `drush deploy`.
4uk4 avatar
флаг cn
Если ошибка возникает в функции Drupal, используемой в разных местах, вы должны проверить стек вызовов, откуда она вызывается. Это может указывать на модуль, который подключается к процессу сохранения узла.
liquidcms avatar
флаг us
Хм, как ни странно, я больше не получаю уведомления от Drupal Answers.. @sonfd, да, любой тип контента.leymannx, не знаю, что такое hook_deploy, посмотрю; но hook_post_update указывает, что это специально для таких задач, когда вам нужно обновить содержимое сайта, и примеры даже показывают использование пакетного сохранения и сохранения узла: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Extension %21module.api.php/function/hook_post_update_NAME/9.0.x
liquidcms avatar
флаг us
@ 4uk4, да, это был мой следующий план ... отладить в ядре, чтобы увидеть, где это не удается, и посмотреть, могу ли я определить, какой модуль вызывает это. Я удалил весь свой пользовательский код (у которого было сохранение узла), и это не было проблемой. Странно, что нет способа запустить обновление, которое могло бы обойти эту проблему, поскольку кажется глупым исправлять дюжину (да, скорее всего, не так много) модулей contrib для простого обновления контента. В качестве альтернативы я могу дать своим клиентам кнопку для нажатия после обновления сайта; но, конечно, регрессия от D7, что я не могу заставить свой сценарий развертывания выполнить обновление.
liquidcms avatar
флаг us
@leymannz, нет такой вещи, как hook_deploy - возможно, что-то добавлено модулем contrib? Развернуть наверное?
флаг cn
Это крючок для драша @liquidcms
Рейтинг:2
флаг cn

Вы можете воспроизвести ошибку с помощью Drush:

# драш php

>>> $build = ['#markup' => 'ТЕСТ'];
=> [
     "#разметка" => "ТЕСТ",
   ]

>>> $rendered = \Drupal::service('renderer')->render($build);
LogicException с сообщением «Контекст рендеринга пуст, потому что render() был вызван вне вызова renderRoot() или renderPlain(). Вместо этого используйте renderPlain()/renderRoot() или #lazy_builder/#pre_render».

>>> $rendered = \Drupal::service('рендерер')->renderPlain($build);
=> Drupal\Core\Render\Markup {#4668
     разметка: "ТЕСТ",
   }

Таким образом, в сообщении об ошибке говорится, что модули, которые подключаются к процессу сохранения узла, должны вместо этого использовать renderPlain(). Они не должны предполагать, что узлы всегда сохраняются в контексте рендеринга тематической страницы Drupal.

Пример кода для изолированного рендеринга представления:

$build = $view->buildRenderable();
$rendered = \Drupal::service('рендерер')->renderPlain($build);
liquidcms avatar
флаг us
Оказывается, это мой пользовательский код, вызывающий проблему. У меня есть код, который выполняется при сохранении/предварительном сохранении узла, который пытается получить значения из представления и использует advancedRender(), который, как я полагаю, вызывает эту проблему. Разочаровывает то, что мой код работает как надо, когда узлы нормально сохраняются, но в post_update я не могу это сделать. Достаточно легко обойти проблему, так как эти функции не нужно запускать во время этого обновления, но странно, что мне нужно было бы сделать этот хак.
4uk4 avatar
флаг cn
Если функция render() не находится под вашим контролем, как здесь, в представлении, вы можете поместить код в свой собственный RenderContext и отбросить всплывающие метаданные. Drupal делает это, например, при рендеринге писем. См. https://drupal.stackexchange.com/questions/245715/how-to-get-cache-metadata-from-nested-render-array-when-returning-response.
liquidcms avatar
флаг us
спасибо за совет .. полезно знать. Хотя много работы, чтобы просто скопировать поле в новое поле как 1 раз. На данный момент я просто устанавливаю статическую переменную в сценарии обновления, а затем использую ее, чтобы игнорировать мои функции сохранения/предварительного сохранения, поскольку их не нужно запускать при запуске этого обновления.
4uk4 avatar
флаг cn
Хорошо, но это не решает проблему с утечкой метаданных в неизвестный контекст рендеринга. Запуск render() или код, содержащий render(), должен всплывать метаданные только в том случае, если вы можете убедиться, что контекст рендеринга принадлежит отображаемому содержимому. В большинстве случаев это контекст рендеринга страницы при оформлении элементов страницы.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.