Как исправить фильтрацию по иерархии категорий в магазине woocommerce (нормализация БД)
Если у вас возникает проблема, когда фильтрация по родительским категориям не даёт результатов, хотя товары привязаны к дочерним категориям, это руководство поможет это исправить.
Проблема
Симптом: При фильтрации по родительской категории не показывается ни одного товара, хотя товары есть в её дочерних категориях.
Пример:
- Структура категорий:
Federal Actions > SEC > Enforcement - Товар привязан к:
Enforcement(только дочерняя категория) - Фильтрация по
Federal ActionsилиSECвозвращает: Нет результатов - Фильтрация по
Enforcementвозвращает: Товар показан
Почему это происходит
WordPress/WooCommerce требует, чтобы товары были явно отмечены всеми родительскими категориями в иерархии, а не только дочерней категорией.
Когда вы привязываете товар к дочерней категории через админ-интерфейс WordPress, WooCommerce не отмечает товар автоматически всеми родительскими категориями в таблице связей терминов. Это требование структуры данных, а не ограничение HUSKY.
Правильная привязка категорий должна включать:
- Дочернюю категорию:
Enforcement - Родительскую категорию:
SEC - Категорию верхнего уровня:
Federal Actions
Как проверить, есть ли у вас эта проблема
- Перейдите на страницу редактирования товара:
wp-admin/post.php?post=YOUR_PRODUCT_ID&action=edit - Посмотрите на метабокс Categories справа
- Проверьте, отмечены ли все родительские категории в иерархии
- Если отмечена только дочерняя категория → у вас есть эта проблема
Решение: автоматическая нормализация иерархии категорий
Это решение состоит из двух частей:
- Решение на будущее: автоматически отмечать родительские категории при сохранении товаров
- Немедленное решение: нормализовать все существующие товары
Часть 1: автоматическая отметка родительских категорий (будущие товары)
Добавьте этот код в functions.php вашей дочерней темы:
/**
* Automatically mark all parent categories when product is saved
* This ensures hierarchy filtering works correctly
*/
add_action('save_post_product', 'auto_mark_parent_categories', 10, 1);
function auto_mark_parent_categories($product_id) {
// Clear cache
clean_object_term_cache($product_id, 'product_cat');
// Get currently assigned categories
$terms = wp_get_post_terms($product_id, 'product_cat', ['fields' => 'ids']);
if (!is_wp_error($terms) AND !empty($terms)) {
$all_terms = [];
// For each assigned category, get all its parents
foreach ($terms as $term_id) {
$all_terms[] = $term_id;
// Get all ancestor categories
$ancestors = get_ancestors($term_id, 'product_cat', 'taxonomy');
if (!empty($ancestors)) {
$all_terms = array_merge($all_terms, $ancestors);
}
}
// Assign product to all categories including parents
wp_set_object_terms($product_id, array_unique($all_terms), 'product_cat');
// Clear caches
clean_post_cache($product_id);
clean_object_term_cache($product_id, 'product_cat');
if (function_exists('wc_delete_product_transients')) {
wc_delete_product_transients($product_id);
}
}
}
Что это делает:
- Срабатывает каждый раз при сохранении товара
- Получает все привязанные категории
- Находит все родительские категории для каждой привязанной категории
- Отмечает товар полной иерархией
- Очищает все соответствующие кэши
Результат: С этого момента любой сохранённый товар будет автоматически включать все родительские категории.
Часть 2: нормализация существующих товаров (одноразовое исправление)
Этот скрипт исправит все существующие товары в вашей базе данных. Добавьте этот временный код в ваш functions.php:
/**
* One-time script to normalize all existing products
* Access: yourdomain.com/?normalize_cats=1 (must be logged in as admin)
*/
add_action('init', function () {
if (isset($_GET['normalize_cats']) AND current_user_can('manage_options')) {
$result = normalize_all_products_categories();
echo '<h2>Category Normalization Results</h2>';
echo '<pre>';
print_r($result);
echo '</pre>';
die();
}
});
function normalize_all_products_categories() {
global $wpdb;
// Get all product IDs
$args = [
'post_type' => 'product',
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids'
];
$product_ids = get_posts($args);
$log = [];
$normalized_count = 0;
foreach ($product_ids as $product_id) {
// Get current categories
$terms = wp_get_post_terms($product_id, 'product_cat', ['fields' => 'ids']);
if (!is_wp_error($terms) AND !empty($terms)) {
$all_terms = [];
// Collect all parent categories
foreach ($terms as $term_id) {
$all_terms[] = $term_id;
// Get ancestors
$ancestors = get_ancestors($term_id, 'product_cat', 'taxonomy');
if (!empty($ancestors)) {
$all_terms = array_merge($all_terms, $ancestors);
}
}
$all_terms = array_unique($all_terms);
// Only update if we're adding new parent categories
if (count($all_terms) > count($terms)) {
wp_set_object_terms($product_id, $all_terms, 'product_cat');
$normalized_count++;
$log[] = "Product #{$product_id}: added " . (count($all_terms) - count($terms)) . " parent categories";
}
}
}
// Log to WordPress error log
error_log("=== Category Normalization Complete ===");
error_log(implode("\n", $log));
error_log("Total normalized: {$normalized_count} products");
return [
'success' => true,
'normalized' => $normalized_count,
'total' => count($product_ids),
'details' => $log
];
}
Как использовать:
- Добавьте код в ваш
functions.php - Перейдите по адресу:
https://yoursite.com/?normalize_cats=1(нужно быть авторизованным как администратор) - Подождите завершения скрипта
- Вы увидите результаты с информацией о том, сколько товаров было обновлено
- Удалите этот код после однократного запуска
Ожидаемый результат:
Category Normalization Results
Array
(
[success] => 1
[normalized] => 156
[total] => 919
)
Это означает, что у 156 товаров из 919 не было родительских категорий, и это было исправлено.
Проверка исправления
После реализации обеих частей:
- Перейдите на страницу вашего магазина с фильтрами HUSKY
- Попробуйте отфильтровать по родительской категории
- Теперь должны появиться товары из дочерних категорий
- Попробуйте углубляться по иерархии — все уровни должны работать
Почему это не сделано в HUSKY по умолчанию?
Это закономерный вопрос. Вот причины:
- Изменение структуры данных: это изменяет основные связи данных WooCommerce
- Не универсально: некоторые сайты намеренно привязывают только дочерние категории по своей бизнес-логике
- Производительность: на очень больших каталогах это добавляет нагрузку при каждом сохранении товара
- Конфликты: может мешать другим плагинам, управляющим связями категорий
Будущее улучшение: мы можем добавить это как опциональную настройку в HUSKY, которую пользователи смогут включить при необходимости, но это не будет поведением по умолчанию.
Альтернатива: ручная привязка категорий
Если вы предпочитаете ручной контроль, можно:
- Редактировать каждый товар
- В метабоксе Categories вручную отметить все нужные родительские категории
- Сохранить товар
Это даёт детальный контроль, но требует много времени для больших каталогов.
Решение проблем
Скрипт показывает 0 нормализованных товаров
- Возможно, у ваших товаров уже правильно привязаны родительские категории
- Проверьте несколько товаров вручную
Фильтрация всё равно не работает после нормализации
- Очистите все кэши (WordPress, сервер, браузер)
- Убедитесь, что HUSKY использует правильную таксономию (
product_cat) - Проверьте конфликты с другими плагинами фильтрации
- Убедитесь, что товары опубликованы (не черновики)
Товары пропадают из фильтра дочерней категории
Этого не должно происходить, но если это случилось:
- Скрипт сохраняет все существующие привязки категорий
- Проверьте, не мешает ли другой плагин
- Восстановите из резервной копии и обратитесь в поддержку
Пользовательские таксономии
Это решение также работает для пользовательских таксономий, созданных с CPT UI или похожими плагинами. Просто замените 'product_cat' на slug вашей пользовательской таксономии:
// Example for custom taxonomy 'modality'
$terms = wp_get_post_terms($product_id, 'modality', ['fields' => 'ids']);
wp_set_object_terms($product_id, array_unique($all_terms), 'modality');
Важные замечания
- Требуется дочерняя тема: всегда используйте дочернюю тему, чтобы не потерять код при обновлении темы
- Сначала резервная копия: сделайте резервную копию базы данных перед запуском скрипта нормализации
- Тестируйте на staging: если возможно, сначала протестируйте на тестовом сайте
- Одноразовый скрипт: удалите скрипт нормализации после однократного запуска
- Сохраните код части 1: хук
save_post_productдолжен оставаться навсегда
Итог
Добавить в functions.php навсегда:
- Хук автоматической отметки родительских категорий (Часть 1)
Запустить один раз и удалить:
- Скрипт нормализации (Часть 2)
Ожидаемые результаты:
- Фильтрация по родительской категории работает правильно
- Фильтрация по дочерней категории продолжает работать
- Углубление по иерархии работает корректно
- Будущие товары автоматически включают родительские категории