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

如何在Drupal 8中检测由Cron触发的节点发布事件?

解决Drupal中识别Cron触发的节点自动发布事件

我来帮你搞定这个问题!你需要实现两个核心需求:创建文章时自动设置次日发布时间,以及仅在Cron自动发布该文章时触发外部请求。下面是分步解决方案:

1. 创建文章时自动设置次日发布时间

我们可以使用hook_entity_presave在节点保存前自动配置发布时间,同时给节点标记一个“需要触发请求”的标识(这里用自定义布尔字段举例,也可以用节点私有数据)。

首先,在Drupal后台给文章内容类型添加一个布尔类型的自定义字段(比如命名为field_trigger_webhook),设置默认值为TRUE,这样新创建的文章会自动带上这个标记。

然后在自定义模块中实现钩子:

/**
 * Implements hook_entity_presave().
 */
function mymodule_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
  // 仅处理新创建的文章节点
  if ($entity->getEntityTypeId() === 'node' && $entity->bundle() === 'article' && $entity->isNew()) {
    // 设置发布时间为次日凌晨0点
    $tomorrow_timestamp = strtotime('tomorrow');
    $entity->set('published_at', $tomorrow_timestamp);
    
    // 确保标记字段为TRUE(如果后台默认值没生效的话)
    $entity->set('field_trigger_webhook', TRUE);
  }
}

2. 识别Cron触发的节点发布事件

关键在于区分“用户手动更新”和“Cron自动发布”。Drupal在Cron运行期间会设置一个状态标记system.cron_running,我们可以利用这个标记结合节点状态的变化来精准判断。

实现hook_entity_update钩子:

/**
 * Implements hook_entity_update().
 */
function mymodule_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
  // 过滤非文章节点
  if ($entity->getEntityTypeId() !== 'node' || $entity->bundle() !== 'article') {
    return;
  }

  // 获取节点原始状态和当前状态
  $original_status = $entity->original->get('status')->value;
  $current_status = $entity->get('status')->value;

  // 检查:从未发布(0)变为已发布(1),且当前处于Cron运行中,同时节点标记了需要触发请求
  if ($original_status === 0 && $current_status === 1 && \Drupal::state()->get('system.cron_running') && $entity->get('field_trigger_webhook')->value) {
    // 发送请求到外部服务器
    $http_client = \Drupal::httpClient();
    try {
      $response = $http_client->post('https://your-target-server.com/webhook-endpoint', [
        'json' => [
          'node_id' => $entity->id(),
          'node_title' => $entity->getTitle(),
          'published_time' => $entity->get('published_at')->value,
        ],
      ]);

      // 请求成功后,将标记设为FALSE,避免重复触发
      $entity->set('field_trigger_webhook', FALSE);
      $entity->save();
    }
    catch (\GuzzleHttp\Exception\RequestException $e) {
      // 记录请求失败日志
      \Drupal::logger('mymodule')->error('Failed to send webhook for node @id: @error', [
        '@id' => $entity->id(),
        '@error' => $e->getMessage(),
      ]);
    }
  }
}

替代方案:直接在Cron钩子中处理

如果你不想依赖Drupal自带的节点自动发布机制,也可以在hook_cron中主动查询待发布的文章,完成发布+请求发送:

/**
 * Implements hook_cron().
 */
function mymodule_cron() {
  $current_time = \Drupal::time()->getRequestTime();
  
  // 查询所有未发布、发布时间已到、且标记了需要触发请求的文章
  $nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
    'type' => 'article',
    'status' => 0,
    'field_trigger_webhook' => TRUE,
  ]);

  foreach ($nodes as $node) {
    $publish_time = $node->get('published_at')->value;
    if ($publish_time <= $current_time) {
      // 发布节点
      $node->set('status', 1);
      $node->save();

      // 发送外部请求
      $http_client = \Drupal::httpClient();
      try {
        $http_client->post('https://your-target-server.com/webhook-endpoint', [
          'json' => [
            'node_id' => $node->id(),
            'node_title' => $node->getTitle(),
            'published_time' => $publish_time,
          ],
        ]);

        // 标记为已处理
        $node->set('field_trigger_webhook', FALSE);
        $node->save();
      }
      catch (\GuzzleHttp\Exception\RequestException $e) {
        \Drupal::logger('mymodule')->error('Webhook failed for node @id: @msg', [
          '@id' => $node->id(),
          '@msg' => $e->getMessage(),
        ]);
      }
    }
  }
}

注意事项

  • 确保你的自定义模块有http_client服务的访问权限,以及修改文章节点的权限。
  • 如果使用自定义字段,记得在后台配置字段时,把它设置为“隐藏”(不需要让用户看到)。
  • 日志记录很重要,方便排查请求失败的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:26:02