Введение
В стандартной реализации WooCommerce блок «Похожие товары» (Related Products) подбирает позиции хаотично — смешивая теги и случайные категории. В магазинах с глубокой иерархией категорий это приводит к тому, что покупателю худи предлагаются кроссовки или аксессуары.
- Проблема: стандартный алгоритм игнорирует вложенность категорий; если в конечной подкатегории мало товаров, блок либо пустует, либо заполняется нерелевантным ассортиментом со всего магазина.
- Решение: переопределить алгоритм выборки через фильтр
woocommerce_related_products: отдавать приоритет конечным (leaf) подкатегориям, а при нехватке лимита — последовательно подниматься по дереву родителей (ancestors). - Результат: 100% заполняемость блока похожих товаров с максимальной релевантностью предложений и сохранением нативной структуры шаблонов WooCommerce.
1. Реализация через фильтр
Код перехватывает массив идентификаторов похожих товаров перед их рендером и формирует выборку с учетом дерева таксономий product_cat:
function custom_related_products_by_hierarchy( $related_posts, $product_id, $args ) {
$limit = $args['limit'] ?? 4;
$exclude = $args['excluded_ids'] ?? [];
$terms = get_the_terms( $product_id, 'product_cat' );
if ( ! $terms || is_wp_error( $terms ) ) {
return $related_posts;
}
// Определяем листовые (конечные) категории без потомков в текущем наборе
$leaf_terms = [];
$parent_ids = wp_list_pluck( $terms, 'parent' );
foreach ( $terms as $term ) {
if ( ! in_array( $term->term_id, $parent_ids, true ) ) {
$leaf_terms[] = $term;
}
}
// Собираем цепочку slug: сначала конечные категории, затем их родители вверх по иерархии
$slugs = [];
foreach ( $leaf_terms as $term ) {
$slugs[] = $term->slug;
$ancestors = get_ancestors( $term->term_id, 'product_cat' );
foreach ( $ancestors as $parent_id ) {
$parent = get_term( $parent_id, 'product_cat' );
if ( $parent && ! is_wp_error( $parent ) ) {
$slugs[] = $parent->slug;
}
}
}
$slugs = array_values( array_unique( $slugs ) );
// Последовательно добираем товары до нужного лимита
$found = [];
foreach ( $slugs as $slug ) {
$need = $limit - count( $found );
if ( $need <= 0 ) {
break;
}
$ids = wc_get_products( [
'status' => 'publish',
'limit' => $need,
'return' => 'ids',
'orderby' => 'rand',
'category' => [ $slug ],
'exclude' => array_merge( $exclude, $found ),
] );
$found = array_merge( $found, $ids );
}
return ! empty( $found ) ? array_slice( array_values( array_unique( $found ) ), 0, $limit ) : $related_posts;
}
add_filter( 'woocommerce_related_products', 'custom_related_products_by_hierarchy', 10, 3 );
2. Сценарии вложенности категорий
Алгоритм корректно обрабатывает оба распространенных сценария наполнения каталога в интернет-магазинах:
-
Товар привязан ко всем уровням категорий
Пример:Одежда → Мужская одежда → Толстовки. Алгоритм выделит категорию «Толстовки» как конечную, заполнит блок товарами из нее, и только если позиций меньше лимита — обратится к «Мужской одежде». -
Товар привязан только к конечной категории
Пример: в карточке товара отмечена только подкатегорияТолстовки. Функция автоматически найдет предков черезget_ancestors(), сохраняя контекст каталога.
3. Вывод и позиционирование в теме
Поскольку логика реализована на уровне фильтра woocommerce_related_products, все стандартные функции вывода WooCommerce продолжают работать без модификации HTML-кода шаблонов:
Стандартный вызов
<?php woocommerce_output_related_products(); ?>
Настройка колонок и лимита
<?php woocommerce_related_products( [
'posts_per_page' => 4,
'columns' => 4,
] ); ?>
Изменение позиции вывода через хуки
Если требуется переместить блок похожих товаров в другое место карточки товара (например, ниже отзывов):
// Удаляем стандартный хук
remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20 );
// Добавляем с новым приоритетом
add_action( 'woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 35 );
Вывод
Кастомная логика подбора через фильтр woocommerce_related_products решает ключевые задачи интернет-магазина:
- Релевантность конверсии: покупатель всегда видит максимально близкие по смыслу товары из текущей подкатегории.
- Отсутствие дыр в верстке: блок никогда не будет пустым, если в каталоге есть смежные товары в родительских категориях.
- Сохранение чистоты темы: логика полностью отделена от представления, не ломает верстку шаблонов и совместима с любыми сторонними темами WooCommerce.

