如何在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
相关产品推荐
相关产品推荐

