You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Drupal 10:hook_entity_insert/update无法为Event类型Paragraph自动打标签求助

Drupal 10模块Paragraph自动打标签问题排查

问题描述

我正在开发一个Drupal 10模块,通过实现hook_entity_insert和hook_entity_update钩子,在节点创建或更新时为其中的event类型Paragraph自动打标签。钩子已触发,但Paragraph并未更新,日志有时会显示‘未找到段落’‘节点未关联组’的信息,然而数据实际存在且无报错。

疑问

  • 是否需要在钩子内重新加载实体(如Paragraph或组)以确保数据最新?
  • hook_entity_insert或hook_entity_update是否存在特殊特性导致该问题?
  • 问题是否出在获取关联实体或组数据的自定义辅助函数中?

相关代码

/**
 * Implements hook_entity_insert().
 */
function my_module_entity_insert(EntityInterface $entity) {
  if ($entity instanceof NodeInterface) {
    my_module_handle_event_paragraph_tagging($entity);
  }
}

/**
 * Implements hook_entity_update().
 */
function my_module_entity_update(EntityInterface $entity) {
  if ($entity instanceof NodeInterface) {
    my_module_handle_event_paragraph_tagging($entity);
  }
}

/**
 * Handles tagging for paragraphs of type 'event' in a given node.
 *
 * @param \Drupal\node\NodeInterface $entity
 *   The node entity being saved. This node contains referenced paragraphs.
 */
function my_module_handle_event_paragraph_tagging(NodeInterface $entity) {
  if (!$entity->hasField('field_content_function')) {
    \Drupal::logger('my_module')->warning('Node @id does not have field_content_function.', ['@id' => $entity->id()]);
    return;
  }

  $paragraphs = FieldHelper::getReferencedEntities($entity, 'field_content_function');
  if (empty($paragraphs)) {
    \Drupal::logger('my_module')->notice('No paragraphs found in field_content_function for node @id.', ['@id' => $entity->id()]);
    return;
  }

  $group = EntityHelper::getGroup($entity); 
  if (!$group) {
    \Drupal::logger('my_module')->warning('Node @id is not associated with a group.', ['@id' => $entity->id()]);
    return;
  }

  $auto_main_tag_groups = FieldHelper::getReferencedEntities($group, 'field_auto_main_tagged_groups') ?? [];
  $auto_main_tag_group_ids = array_map(fn($g) => $g->id(), $auto_main_tag_groups);

  foreach ($paragraphs as $paragraph) {
    if ($paragraph->bundle() !== 'event') {
      continue;
    }

    $main_event_list_groups = FieldHelper::getReferencedEntities($paragraph, 'field_main_tagged_groups') ?? [];
    $main_event_list_group_ids = array_map(fn($g) => $g->id(), $main_event_list_groups);

    $groups_to_add = array_diff($auto_main_tag_group_ids, $main_event_list_group_ids);

    foreach ($groups_to_add as $group_id) {
      $paragraph->get('field_main_tagged_groups')->appendItem(['target_id' => $group_id]);
    }

    try {
      $paragraph->save();
    } catch (\Exception $e) {
      \Drupal::logger('my_module')->error('Error saving paragraph @id: @message', [
        '@id' => $paragraph->id(),
        '@message' => $e->getMessage(),
      ]);
    }
  }
}

/**
 * Retrieves the group associated with a node.
 *
 * @param \Drupal\node\NodeInterface $node
 *   The node entity.
 *
 * @return \Drupal\group\Entity\Group|null
 *   The associated group entity, or NULL if no group is found.
 */
function my_module_get_group(NodeInterface $node) {
  $group = FieldHelper::getReferencedEntities($node, 'field_group');
  if (!empty($group)) {
    return reset($group);
  }

  if ($node->hasField('field_parent_node')) {
    $parent = FieldHelper::getReferencedEntities($node, 'field_parent_node');
    if (!empty($parent) && $parent[0] instanceof NodeInterface) {
      return my_module_get_group($parent[0]); 
    }
  }

  \Drupal::logger('my_module')->warning('No group found for node @id.', ['@id' => $node->id()]);
  return NULL;
}

排查思路与解决方案

1. 实体重新加载的必要性

在hook_entity_insert/update触发时,节点和关联的Paragraph可能还处于内存临时状态,未完全同步到数据库。建议:

  • 处理Paragraph前重新加载实体,确保拿到最新数据:
    $paragraph_storage = \Drupal::entityTypeManager()->getStorage('paragraph');
    $paragraph = $paragraph_storage->load($paragraph->id());
    if (!$paragraph) {
      \Drupal::logger('my_module')->warning('Paragraph @id not found after reload.', ['@id' => $paragraph->id()]);
      continue;
    }
    
  • 在my_module_get_group的递归逻辑中,父节点也需要重新加载,避免使用过期的内存数据。

2. 钩子特性的影响

  • 事务机制:Drupal在节点保存时会开启事务,如果Paragraph的保存操作在节点事务未提交前执行,可能会被回滚。可以将Paragraph的保存逻辑延迟到事务提交后:
    \Drupal::service('database')->addCommitCallback(function() use ($paragraph, $groups_to_add) {
      foreach ($groups_to_add as $group_id) {
        $paragraph->get('field_main_tagged_groups')->appendItem(['target_id' => $group_id]);
      }
      $paragraph->save();
    });
    
  • 钩子触发时机:hook_entity_insert在节点刚创建时触发,此时随节点一起创建的Paragraph可能还未完成保存。可以调整钩子权重,确保在节点完全持久化后再执行:
    function my_module_module_implements_alter(&$implementations, $hook) {
      if ($hook === 'entity_insert' || $hook === 'entity_update') {
        $group = $implementations['my_module'];
        unset($implementations['my_module']);
        $implementations['my_module'] = $group;
      }
    }
    
  • 递归触发问题:调用$paragraph->save()会触发hook_entity_update,可能导致循环。可以在钩子中添加判断:
    if ($entity instanceof ParagraphInterface && $entity->bundle() === 'event') {
      return;
    }
    

3. 自定义辅助函数排查

  • 检查FieldHelper::getReferencedEntities的实现,确保它正确使用$field->referencedEntities()来获取关联实体,而非直接查询数据库(未保存的实体不会出现在数据库中)。
  • 在my_module_get_group中添加日志,输出递归过程中的节点ID,确认是否正确找到父节点及关联组:
    \Drupal::logger('my_module')->debug('Checking parent node @pid for group.', ['@pid' => $parent[0]->id()]);
    
  • 验证field_content_function、field_group等字段的机器名是否与配置一致,避免拼写错误。

4. 其他检查点

  • 确认执行钩子的用户(通常是系统用户)拥有编辑event类型Paragraph的权限,否则保存会静默失败。
  • 在$paragraph->save()前后输出字段值,确认修改是否生效:
    \Drupal::logger('my_module')->debug('Before save: @values', ['@values' => print_r($paragraph->get('field_main_tagged_groups')->getValue(), TRUE)]);
    
  • 检查Paragraph的字段配置,确保field_main_tagged_groups允许添加多个值,且目标类型为Group。

内容的提问来源于stack exchange,提问作者Manogna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 21:30:55