Диагностика проблемы: почему родительская категория не устанавливается автоматически
В WooCommerce часто возникает задача автоматического присвоения родительской категории товара при изменении или обновлении записи. Это особенно актуально при сложной иерархии категорий, когда у товара может быть выбрана дочерняя категория, а родительская не установлена явно.
Без автоматической установки родительской категории фильтры и навигация могут работать некорректно, а пользователи не увидят товары в родительских категориях.
Проверить проблему можно так:
- Открыть карточку товара в админке WordPress.
- Убедиться, что выбрана только дочерняя категория.
- Перейти на фронтенд и открыть страницу родительской категории.
- Если товар не отображается, значит родительская категория не установлена.
Пошаговое решение: добавляем хук для автоматического присвоения
Реализуем функцию, которая при сохранении товара будет проверять выбранные категории и автоматически добавлять родительскую категорию, если она отсутствует.
Добавьте следующий код в functions.php вашей темы или в кастомный плагин:
add_action('save_post_product', 'wc_set_parent_category_on_save', 20, 3);
function wc_set_parent_category_on_save($post_id, $post, $update) {
// Проверяем, что это не автосохранение
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Проверяем права пользователя
if (!current_user_can('edit_post', $post_id)) {
return;
}
// Получаем текущие категории товара
$terms = wp_get_post_terms($post_id, 'product_cat');
if (is_wp_error($terms) || empty($terms)) {
return; // Категорий нет
}
$parent_cats_to_add = [];
foreach ($terms as $term) {
if ($term->parent > 0) {
$parent_cats_to_add[] = $term->parent;
}
}
if (empty($parent_cats_to_add)) {
return; // Родительские категории не нужны
}
// Получаем все категории товара с учетом новых родительских
$all_cats = wp_get_post_terms($post_id, 'product_cat', ['fields' => 'ids']);
$new_parents = array_diff($parent_cats_to_add, $all_cats);
if (!empty($new_parents)) {
$updated_cats = array_merge($all_cats, $new_parents);
wp_set_post_terms($post_id, $updated_cats, 'product_cat');
}
}Объяснение кода
- Хук
save_post_productсрабатывает при сохранении товара. - Функция получает текущие категории товара.
- Определяет родительские категории для выбранных дочерних.
- Добавляет родительские категории к товару, если их там нет.
Проверка результата после внедрения
Чтобы проверить, что автоматическое добавление работает, выполните следующие шаги:
- Откройте или создайте новый товар в админке и выберите только дочернюю категорию.
- Сохраните товар.
- В админке снова откройте товар в разделе "Категории товара" — теперь должны отображаться и родительские категории.
- На фронтенде откройте страницу родительской категории — ваш товар должен отображаться в списке.
Частые ошибки и как их исправить
- Автосохранение перезаписывает данные: функция не учитывает
DOING_AUTOSAVE. Решение: добавьте проверкуif (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;. - Нет прав на редактирование: функция срабатывает без проверки прав, что может вызвать ошибки. Добавьте проверку
current_user_can('edit_post', $post_id). - Бесконечный цикл обновления: при вызове
wp_set_post_termsвнутри хука сохранения может происходить повторный вызов. В данном коде этого избегаем, так как хук с приоритетом 20 и проверками. - Категории не обновляются после импорта: если импорт идет напрямую через базу или сторонние плагины, данный хук может не сработать. В этом случае нужно интегрировать логику в процесс импорта.
Практические советы по производительности и безопасности
- Не выполняйте тяжелые запросы внутри хука
save_post_product, избегайте лишних вызовов API. - Кэшируйте результаты, если обрабатываете большое количество товаров.
- Защищайте функцию проверкой прав и автосохранения, чтобы не создавать уязвимости.
- Для массовых операций (импорт, обновление) лучше использовать отдельные скрипты с оптимизированной логикой.
Сравнение вариантов автоматического добавления родительских категорий
| Метод | Плюсы | Минусы | Пример кода/плагина |
|---|---|---|---|
Хук save_post_product |
Автоматизация при сохранении товара, работает с любым интерфейсом редактирования | Не срабатывает на массовые импорты без адаптации, возможен повторный вызов | Код в статье |
| Плагин с интеграцией в импорт | Обеспечивает настройку и контроль в одном месте, работает с CSV и XML | Зависимость от сторонних решений, возможны конфликты | Зависит от плагина, например WP All Import с кастомными функциями |
| Ручная установка через админку | Простота и контроль | Много времени при большом количестве товаров | Не требует кода |
Дополнительный пример: удаление лишних категорий и оптимизация
Если товар уже имеет родительскую категорию и вы хотите, чтобы при сохранении оставалась только самая верхняя категория из иерархии, добавьте такую функцию:
add_action('save_post_product', 'wc_optimize_categories_on_save', 25, 3);
function wc_optimize_categories_on_save($post_id, $post, $update) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $post_id)) return;
$terms = wp_get_post_terms($post_id, 'product_cat');
if (is_wp_error($terms) || empty($terms)) return;
// Находим корневые категории из текущих
$root_cats = [];
foreach ($terms as $term) {
$parent = $term->parent;
while ($parent != 0) {
$parent_term = get_term($parent, 'product_cat');
if (is_wp_error($parent_term) || !$parent_term) break;
$parent = $parent_term->parent;
}
$root_cats[$term->term_id] = $parent === 0 ? $term->term_id : $parent;
}
$unique_roots = array_unique($root_cats);
wp_set_post_terms($post_id, $unique_roots, 'product_cat');
}