Проблема: необходимость удаления товаров после первого заказа
В WooCommerce иногда возникает задача автоматически удалять товары из каталога, если кто-то совершил их покупку впервые. Это может быть актуально для ограниченных тиражей, эксклюзивных предложений или акций с единичным доступом к товару.
Диагностика проблемы
По умолчанию WooCommerce не предоставляет возможность автоматически удалять или скрывать товар после первого заказа. Для реализации такой логики нужно вмешаться в процесс обработки заказов и проверить, был ли товар уже куплен.
Основные вопросы для диагностики:
- Как отследить первый заказ товара?
- В какой момент удалять или скрывать товар?
- Как корректно обновить данные, чтобы не сломать логику магазина?
Пошаговое решение: реализация автоматического удаления товаров
1. Отслеживание первого заказа товара
Для этого используем хук woocommerce_order_status_completed, который срабатывает при смене статуса заказа на «завершён». В обработчике проверяем, есть ли в базе другие заказы с этим товаром.
2. Логика удаления товара
Если товар был куплен впервые, удаляем его программно через функцию wp_delete_post(). Для избежания потери данных можно рассмотреть вариант изменения статуса товара на «черновик» или «вне каталога».
3. Пример кода
add_action('woocommerce_order_status_completed', 'auto_remove_products_after_first_order', 10, 1);
function auto_remove_products_after_first_order($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
foreach ($order->get_items() as $item) {
$product_id = $item->get_product_id();
// Ищем другие завершённые заказы с этим товаром, кроме текущего
$args = array(
'limit' => 1,
'status' => 'completed',
'return' => 'ids',
'exclude' => array($order_id),
'meta_query' => array(
array(
'key' => '_product_id',
'value' => $product_id,
'compare' => '=',
),
),
);
// Получаем заказы с этим товаром
$orders_with_product = wc_get_orders($args);
// Если других заказов с этим товаром нет — удаляем
if (empty($orders_with_product)) {
// Вместо удаления можно переводить в черновик: wp_update_post(array('ID' => $product_id, 'post_status' => 'draft'));
wp_delete_post($product_id, true);
}
}
}
Важно: В meta_query выше условие упрощено, поскольку в заказах товар хранится не как мета, а в item meta. Для корректного поиска понадобится более сложный SQL-запрос. Ниже - более точный метод.
Уточнённый метод проверки покупки товара
function has_other_completed_orders_with_product($product_id, $exclude_order_id) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT order_items.order_id
FROM {$wpdb->prefix}woocommerce_order_items AS order_items
JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS itemmeta
ON order_items.order_item_id = itemmeta.order_item_id
JOIN {$wpdb->posts} AS posts
ON order_items.order_id = posts.ID
WHERE posts.post_status = 'wc-completed'
AND order_items.order_item_type = 'line_item'
AND itemmeta.meta_key = '_product_id'
AND itemmeta.meta_value = %d
AND order_items.order_id != %d
LIMIT 1",
$product_id,
$exclude_order_id
);
$result = $wpdb->get_var($query);
return !empty($result);
}
add_action('woocommerce_order_status_completed', 'auto_remove_products_after_first_order_refined', 10, 1);
function auto_remove_products_after_first_order_refined($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
foreach ($order->get_items() as $item) {
$product_id = $item->get_product_id();
if (!has_other_completed_orders_with_product($product_id, $order_id)) {
wp_delete_post($product_id, true); // или wp_update_post(array('ID' => $product_id, 'post_status' => 'draft'));
}
}
}
Проверка результата после внедрения
- Создайте тестовый товар в WooCommerce.
- Сделайте заказ с этим товаром и переведите его в статус «Завершён».
- Проверьте, удалён ли товар из каталога (через админку или URL товара).
- Попробуйте сделать повторный заказ того же товара — он должен быть недоступен.
Если товар удаляется или становится недоступен после первого завершённого заказа — решение работает.
Частые ошибки и как их исправить
- Не удаляется товар после заказа: Проверьте, что хук
woocommerce_order_status_completedсрабатывает, а функцияwp_delete_post()вызывается с правильным ID. - Удаляются все товары сразу: Ошибка в логике проверки предыдущих заказов. Используйте SQL-запрос с join по order_items и order_itemmeta, как в примере выше.
- Проблемы с кешированием: После удаления товара очистите кеш сайта и браузера, чтобы увидеть изменения.
- Нарушается целостность данных: Вместо удаления используйте изменение статуса на
draftилиprivate.
Практические советы по безопасности и производительности
- Безопасность: Убедитесь, что код выполняется только в нужных условиях — используйте проверки прав пользователя и nonce, если добавляете UI для управления.
- Производительность: SQL-запросы к заказам могут быть тяжёлыми при большом объёме данных. Кешируйте результаты или используйте транзиенты, если возможно.
- Резервное копирование: Перед автоматическим удалением товаров сделайте резервную копию базы данных на случай ошибок.
Сравнение вариантов удаления товара после первого заказа
| Метод | Плюсы | Минусы | Рекомендации |
|---|---|---|---|
Удаление товара (wp_delete_post()) | Полное удаление, освобождение места | Потеря данных, нельзя восстановить без бэкапа | Использовать с осторожностью, лучше на небольших магазинах |
| Смена статуса на «черновик» | Товар скрыт, данные сохранены | Товар остаётся в базе, занимает место | Рекомендуется для безопасности и простоты восстановления |
| Использование кастомного поля для скрытия | Гибкость в управлении отображением | Требует дополнительной логики в шаблонах | Подходит для сложных сценариев |