WooCommerce如何移除已停用活动分类产品的待发送定时邮件
问题核心原因
你现有代码的逻辑存在两个本质问题:
woocommerce_is_purchasable钩子仅在前端判断商品是否可购买时触发,订单完成、Cron任务执行等场景根本不会运行该钩子内的逻辑,所以你写的remove_action几乎不会生效- 已经通过
wp_schedule_event或wp_schedule_single_event排入WordPress Cron队列的任务,不会因为你移除了Action就自动删除,已经入队的任务到期后还是会尝试执行
修复方案
你需要从三个层面调整逻辑,覆盖所有场景:
1. 兜底拦截:所有邮件发送前强制校验活动有效性
这是最稳妥的方案,不管是新调度还是已经入队的邮件,发送前都会先判断对应活动是否属于失效分类,是就直接终止发送,不会漏发:
// 拦截提醒邮件发送 add_action('wooevent_email_reminder', 'check_deactivated_event_before_send', 1, 4); add_action('wooevent_email_reminder_2', 'check_deactivated_event_before_send', 1, 4); add_action('wooevent_email_reminder_3', 'check_deactivated_event_before_send', 1, 4); function check_deactivated_event_before_send($event_id, $order_id, $attendee_id, $template_id) { // 替换为你实际的失效分类别名,你代码里写的是seminar-deaktiviert,注意匹配 if (has_term('event-deactivated', 'product_cat', $event_id)) { // 直接移除后续的发送动作 global $WooEvent_Reminder_Email; remove_action(current_action(), array($WooEvent_Reminder_Email, 'send_mail'), 10); } } // 拦截反馈邮件发送 add_action('wooevent_email_reminder_feedback', 'check_deactivated_event_before_send_fb', 1, 3); function check_deactivated_event_before_send_fb($event_id, $order_id, $template_id) { if (has_term('event-deactivated', 'product_cat', $event_id)) { global $WooEvent_Reminder_Email; remove_action(current_action(), array($WooEvent_Reminder_Email, 'send_mail_fb'), 10); } } // 拦截参会人邮件发送 add_action('we_send_email_reminder', 'check_deactivated_event_before_send_attendee', 1, 1); function check_deactivated_event_before_send_attendee($event_data) { $event_id = $event_data['event_id'] ?? 0; if ($event_id && has_term('event-deactivated', 'product_cat', $event_id)) { global $WooEvent_Reminder_Email; remove_action(current_action(), array($WooEvent_Reminder_Email, 'send_mail_attendee'), 10); } }
2. 阻止新订单生成邮件调度任务
不需要在woocommerce_is_purchasable里移除动作,直接在调度动作执行前判断订单内是否包含失效活动即可:
add_action('woocommerce_order_status_completed', 'block_deactivated_event_email_schedule', 9, 1); function block_deactivated_event_email_schedule($order_id) { $order = wc_get_order($order_id); foreach ($order->get_items() as $item) { $product_id = $item->get_product_id(); if (has_term('event-deactivated', 'product_cat', $product_id)) { global $WooEvent_Reminder_Email; remove_action('woocommerce_order_status_completed', array($WooEvent_Reminder_Email, 'remind_email'), 10); remove_action('woocommerce_order_status_completed', array($WooEvent_Reminder_Email, 'remind_email_fb'), 11); break; } } }
3. 清理已经入队的失效活动邮件任务
你可以运行一次以下代码清理现有队列,运行后可以删除:
add_action('admin_init', 'clear_deactivated_event_cron_jobs'); function clear_deactivated_event_cron_jobs() { // 仅管理员运行一次,运行后注释掉即可 if (!current_user_can('manage_options')) return; $cron_jobs = _get_cron_array(); $email_hooks = [ 'wooevent_email_reminder', 'wooevent_email_reminder_2', 'wooevent_email_reminder_3', 'wooevent_email_reminder_feedback', 'we_send_email_reminder' ]; foreach ($cron_jobs as $timestamp => $jobs) { foreach ($jobs as $hook => $events) { if (in_array($hook, $email_hooks)) { foreach ($events as $event) { $event_id = $event['args'][0] ?? 0; if ($event_id && has_term('event-deactivated', 'product_cat', $event_id)) { wp_unschedule_event($timestamp, $hook, $event['args']); } } } } } }
其他注意事项
- 你原有实现分类禁用活动的
woocommerce_is_purchasable钩子代码可以保留,仅删除里面所有remove_action相关的代码即可 - 注意替换代码中
event-deactivated为你实际的产品分类别名,你现有代码中写的是seminar-deaktiviert,保持统一即可 - PHP 7.0+版本调用类方法时不需要加
&引用符,原有代码中的&$WooEvent_Reminder_Email可以去掉&避免报错 - 你原有代码中移除
send_mail_fb时写的参数数量是1,和原注册时的3不一致,会导致移除失败,上述修正代码已经规避了该问题
内容的提问来源于stack exchange,提问作者dig99
相关产品推荐
相关产品推荐

