Диагностика проблемы: зачем и когда нужно удалять товары без заказов
В крупных интернет-магазинах на WooCommerce часто накапливается большое количество товаров, которые никогда не были проданы. Такие позиции занимают место в базе данных, снижают производительность и усложняют управление каталогом. Задача — автоматизировать удаление товаров, у которых нет заказов за определённый период (например, 6 месяцев) без риска случайного удаления актуальных товаров.
Как определить товары без заказов в WooCommerce
Для точной работы нам нужно получить все ID товаров, для которых отсутствуют связанные заказы. В базе WooCommerce связь заказов и товаров хранится в таблицах wp_woocommerce_order_items и wp_woocommerce_order_itemmeta.
Пример SQL-запроса для диагностики товаров без заказов:
SELECT p.ID, p.post_title FROM wp_posts p
WHERE p.post_type = 'product'
AND p.post_status = 'publish'
AND NOT EXISTS (
SELECT 1 FROM wp_woocommerce_order_items oi
JOIN wp_woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
JOIN wp_posts o ON o.ID = oi.order_id
WHERE oim.meta_key = '_product_id'
AND oim.meta_value = p.ID
AND o.post_status IN ('wc-completed', 'wc-processing')
AND o.post_date > DATE_SUB(NOW(), INTERVAL 6 MONTH)
)Этот запрос вернёт все опубликованные товары, которые не были заказаны за последние 6 месяцев в статусах wc-completed или wc-processing.
Пошаговое решение: автоматизация удаления через WP-Cron
1. Создаём функцию для поиска и удаления товаров без заказов
function wpengine_delete_unsold_products() {
global $wpdb;
$interval_months = 6;
$product_ids = $wpdb->get_col($wpdb->prepare(
"SELECT p.ID FROM {$wpdb->posts} p
WHERE p.post_type = 'product'
AND p.post_status = 'publish'
AND NOT EXISTS (
SELECT 1 FROM {$wpdb->prefix}woocommerce_order_items oi
JOIN {$wpdb->prefix}woocommerce_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
JOIN {$wpdb->posts} o ON o.ID = oi.order_id
WHERE oim.meta_key = '_product_id'
AND oim.meta_value = p.ID
AND o.post_status IN ('wc-completed', 'wc-processing')
AND o.post_date > DATE_SUB(NOW(), INTERVAL %d MONTH)
)",
$interval_months
));
if (empty($product_ids)) {
return 'No products to delete';
}
foreach ($product_ids as $product_id) {
wp_delete_post($product_id, true); // true для полного удаления
}
return count($product_ids) . ' products deleted';
}2. Добавляем событие WP-Cron для еженедельного запуска
add_action('wpengine_weekly_cleanup', 'wpengine_delete_unsold_products');
function wpengine_setup_weekly_cron() {
if (!wp_next_scheduled('wpengine_weekly_cleanup')) {
wp_schedule_event(time(), 'weekly', 'wpengine_weekly_cleanup');
}
}
add_action('wp', 'wpengine_setup_weekly_cron');3. Ручной запуск и логирование для проверки
// Для тестирования запускаем функцию напрямую
add_action('admin_init', function() {
if (current_user_can('manage_options') && isset($_GET['run_cleanup'])) {
$result = wpengine_delete_unsold_products();
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html($result) . '</p></div>';
}
});Для запуска вручную добавьте параметр ?run_cleanup=1 к URL админки WordPress. Так можно проверить, что функция работает и удаляет именно нужные товары.
Проверка результата после внедрения
- Запустите функцию вручную через URL с параметром
?run_cleanup=1в панели администратора. - Проверьте, что удалились только товары без заказов за последние 6 месяцев.
- Запустите SQL-запрос из раздела диагностики до и после запуска для сравнения.
- Отследите запись в логах сервера или активируйте WP_DEBUG для выявления ошибок.
Частые ошибки и способы исправления
- Удаляются нужные товары: Проверьте, что фильтр по статусам заказа и дате корректен. Возможно, товары имеют заказы в других статусах (например,
wc-on-hold), их нужно добавить в запрос. - Функция не запускается по расписанию: Убедитесь, что WP-Cron работает (есть трафик на сайт). Запустите вручную
wp_schedule_eventи проверьте расписание через плагин WP Crontrol. - Ошибка прав доступа при удалении товаров: Функция должна выполнять удаление от имени администратора. Запускайте в контексте, где есть нужные права.
- Длительное выполнение и нагрузка на базу: Для больших магазинов разбейте удаление на части — удаляйте по 50-100 товаров за раз с помощью
array_chunkи планируйте повторные вызовы.
Практические советы по безопасности и производительности
- Резервное копирование: Перед внедрением обязательно сделайте бэкап базы данных и файлов.
- Тестирование на staging-сервере: Проверяйте работу скрипта в безопасной среде, чтобы избежать потери данных.
- Оптимизация запросов: Индексы на
wp_woocommerce_order_itemmeta.meta_keyиmeta_valueускорят выборку. - Логирование: Добавьте запись в лог с количеством удалённых товаров для мониторинга.
- Безопасность кода: Не запускайте удаление вне контекста авторизованного администратора.
Сравнение способов автоматизации удаления товаров без заказов
| Метод | Плюсы | Минусы | Компромисс |
|---|---|---|---|
| Плагин (готовые решения) | Быстрая настройка, удобный интерфейс | Может быть тяжёлым, лишние функции, риск конфликтов | Использовать проверенные и лёгкие плагины |
| Кастомный код с WP-Cron | Контроль, эффективность, минимальное влияние | Требует навыков, нужно тестировать | Пошаговое тестирование и логирование |
| Ручное удаление через SQL | Максимальная скорость | Риск ошибок, потеря данных без возврата | Использовать только для диагностики, не для автоматизации |