Ссылка скопирована

Изменение логики похожих товаров в WooCommerce

DonVardix DonVardix

Введение

В стандартной реализации 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. Сценарии вложенности категорий

Алгоритм корректно обрабатывает оба распространенных сценария наполнения каталога в интернет-магазинах:

  1. Товар привязан ко всем уровням категорий
    Пример: Одежда → Мужская одежда → Толстовки. Алгоритм выделит категорию «Толстовки» как конечную, заполнит блок товарами из нее, и только если позиций меньше лимита — обратится к «Мужской одежде».
  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 решает ключевые задачи интернет-магазина:

  1. Релевантность конверсии: покупатель всегда видит максимально близкие по смыслу товары из текущей подкатегории.
  2. Отсутствие дыр в верстке: блок никогда не будет пустым, если в каталоге есть смежные товары в родительских категориях.
  3. Сохранение чистоты темы: логика полностью отделена от представления, не ломает верстку шаблонов и совместима с любыми сторонними темами WooCommerce.