PHPwoocommerceintermediate

WooCommerce Related Products Customization

Customize WooCommerce related products display, algorithm, and layout

Faisal Yaqoob
#woocommerce#related-products#upsell#cross-sell#product-display
Share this snippet:

Code

php
1// Change number of related products displayed
2add_filter('woocommerce_output_related_products_args', 'custom_related_products_args');
3function custom_related_products_args($args) {
4 $args['posts_per_page'] = 8; // Display 8 products
5 $args['columns'] = 4; // Display in 4 columns
6
7 return $args;
8}

Customize how WooCommerce displays related products including the number shown, sorting algorithm, and display layout.

// Change number of related products displayed
add_filter('woocommerce_output_related_products_args', 'custom_related_products_args');
function custom_related_products_args($args) {
    $args['posts_per_page'] = 8; // Display 8 products
    $args['columns'] = 4;        // Display in 4 columns

    return $args;
}
// Customize how related products are selected
add_filter('woocommerce_related_products', 'custom_related_products_algorithm', 10, 3);
function custom_related_products_algorithm($related_posts, $product_id, $args) {
    $product = wc_get_product($product_id);

    if (!$product) {
        return $related_posts;
    }

    // Get products from same categories
    $categories = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids'));

    // Get products from same tags
    $tags = wp_get_post_terms($product_id, 'product_tag', array('fields' => 'ids'));

    $related_args = array(
        'post_type'      => 'product',
        'posts_per_page' => $args['posts_per_page'],
        'post__not_in'   => array($product_id),
        'orderby'        => 'rand',
        'tax_query'      => array(
            'relation' => 'OR',
        )
    );

    // Prioritize same category
    if (!empty($categories)) {
        $related_args['tax_query'][] = array(
            'taxonomy' => 'product_cat',
            'field'    => 'id',
            'terms'    => $categories,
        );
    }

    // Then same tags
    if (!empty($tags)) {
        $related_args['tax_query'][] = array(
            'taxonomy' => 'product_tag',
            'field'    => 'id',
            'terms'    => $tags,
        );
    }

    $related_query = new WP_Query($related_args);
    $related_posts = wp_list_pluck($related_query->posts, 'ID');

    return $related_posts;
}
// Show related products in similar price range
add_filter('woocommerce_related_products', 'related_products_by_price', 10, 3);
function related_products_by_price($related_posts, $product_id, $args) {
    $product = wc_get_product($product_id);

    if (!$product) {
        return $related_posts;
    }

    $price = $product->get_price();

    // Define price range (±20%)
    $min_price = $price * 0.8;
    $max_price = $price * 1.2;

    $related_args = array(
        'post_type'      => 'product',
        'posts_per_page' => $args['posts_per_page'],
        'post__not_in'   => array($product_id),
        'orderby'        => 'rand',
        'meta_query'     => array(
            array(
                'key'     => '_price',
                'value'   => array($min_price, $max_price),
                'compare' => 'BETWEEN',
                'type'    => 'NUMERIC'
            )
        )
    );

    $related_query = new WP_Query($related_args);
    $related_posts = wp_list_pluck($related_query->posts, 'ID');

    return $related_posts;
}

Show Products from Same Brand/Manufacturer

// Display related products from same brand (using custom attribute)
add_filter('woocommerce_related_products', 'related_products_same_brand', 10, 3);
function related_products_same_brand($related_posts, $product_id, $args) {
    $product = wc_get_product($product_id);

    if (!$product) {
        return $related_posts;
    }

    // Get brand attribute
    $brand = $product->get_attribute('brand');

    if (empty($brand)) {
        return $related_posts;
    }

    // Find products with same brand
    $related_args = array(
        'post_type'      => 'product',
        'posts_per_page' => $args['posts_per_page'],
        'post__not_in'   => array($product_id),
        'orderby'        => 'rand',
        'tax_query'      => array(
            array(
                'taxonomy' => 'pa_brand',
                'field'    => 'name',
                'terms'    => $brand,
            )
        )
    );

    $related_query = new WP_Query($related_args);
    $related_posts = wp_list_pluck($related_query->posts, 'ID');

    return $related_posts;
}
// Change related products heading
add_filter('woocommerce_product_related_products_heading', 'custom_related_products_heading');
function custom_related_products_heading() {
    return __('You May Also Like', 'woocommerce');
}
// Remove related products section
remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20);
// Move related products to a different position
remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20);
add_action('woocommerce_after_single_product', 'woocommerce_output_related_products', 10);
// Custom related products display
remove_action('woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20);
add_action('woocommerce_after_single_product_summary', 'custom_related_products_display', 20);

function custom_related_products_display() {
    global $product;

    $related_ids = wc_get_related_products($product->get_id(), 6);

    if (empty($related_ids)) {
        return;
    }

    echo '<section class="related-products-custom">';
    echo '<h2>' . __('Recommended Products', 'woocommerce') . '</h2>';
    echo '<div class="products-grid">';

    foreach ($related_ids as $related_id) {
        $related_product = wc_get_product($related_id);

        if (!$related_product) {
            continue;
        }

        echo '<div class="product-item">';
        echo '<a href="' . get_permalink($related_id) . '">';
        echo $related_product->get_image('medium');
        echo '<h3>' . $related_product->get_name() . '</h3>';
        echo '<span class="price">' . $related_product->get_price_html() . '</span>';
        echo '</a>';
        echo '<a href="?add-to-cart=' . $related_id . '" class="button add-to-cart">' .
             __('Add to Cart', 'woocommerce') . '</a>';
        echo '</div>';
    }

    echo '</div>';
    echo '</section>';
}
// Add custom field to manually select related products
add_action('woocommerce_product_options_related', 'add_custom_related_products_field');
function add_custom_related_products_field() {
    global $post;

    echo '<div class="options_group">';

    ?>
    <p class="form-field">
        <label for="custom_related_products"><?php _e('Custom Related Products', 'woocommerce'); ?></label>
        <select class="wc-product-search" multiple="multiple" style="width: 50%;"
                id="custom_related_products" name="custom_related_products[]"
                data-placeholder="<?php esc_attr_e('Search for products', 'woocommerce'); ?>"
                data-action="woocommerce_json_search_products_and_variations">
            <?php
            $product_ids = get_post_meta($post->ID, '_custom_related_products', true);

            if (!empty($product_ids) && is_array($product_ids)) {
                foreach ($product_ids as $product_id) {
                    $product = wc_get_product($product_id);
                    if ($product) {
                        echo '<option value="' . esc_attr($product_id) . '" selected="selected">' .
                             wp_kses_post($product->get_formatted_name()) . '</option>';
                    }
                }
            }
            ?>
        </select>
    </p>
    <?php

    echo '</div>';
}

// Save custom related products
add_action('woocommerce_process_product_meta', 'save_custom_related_products_field');
function save_custom_related_products_field($post_id) {
    $custom_related = isset($_POST['custom_related_products']) ?
        array_map('intval', $_POST['custom_related_products']) : array();

    update_post_meta($post_id, '_custom_related_products', $custom_related);
}

// Use custom related products if set
add_filter('woocommerce_related_products', 'use_custom_related_products', 10, 3);
function use_custom_related_products($related_posts, $product_id, $args) {
    $custom_related = get_post_meta($product_id, '_custom_related_products', true);

    if (!empty($custom_related) && is_array($custom_related)) {
        return array_slice($custom_related, 0, $args['posts_per_page']);
    }

    return $related_posts;
}

Display Recently Viewed Products

// Track recently viewed products
add_action('woocommerce_before_single_product', 'track_recently_viewed_products');
function track_recently_viewed_products() {
    if (!is_singular('product')) {
        return;
    }

    global $post;

    if (empty($_COOKIE['woocommerce_recently_viewed'])) {
        $viewed_products = array();
    } else {
        $viewed_products = wp_parse_id_list((array) explode('|', wp_unslash($_COOKIE['woocommerce_recently_viewed'])));
    }

    // Remove current product from array if it exists
    $keys = array_flip($viewed_products);
    if (isset($keys[$post->ID])) {
        unset($viewed_products[$keys[$post->ID]]);
    }

    // Add current product to start of array
    array_unshift($viewed_products, $post->ID);

    // Limit to 15 products
    $viewed_products = array_slice($viewed_products, 0, 15);

    // Set cookie
    wc_setcookie('woocommerce_recently_viewed', implode('|', $viewed_products));
}

// Display recently viewed products
function display_recently_viewed_products() {
    if (empty($_COOKIE['woocommerce_recently_viewed'])) {
        return;
    }

    $viewed_products = wp_parse_id_list((array) explode('|', wp_unslash($_COOKIE['woocommerce_recently_viewed'])));

    if (empty($viewed_products)) {
        return;
    }

    // Remove current product
    if (is_singular('product')) {
        global $post;
        $viewed_products = array_diff($viewed_products, array($post->ID));
    }

    if (empty($viewed_products)) {
        return;
    }

    echo '<section class="recently-viewed-products">';
    echo '<h2>' . __('Recently Viewed Products', 'woocommerce') . '</h2>';

    $args = array(
        'post_type'      => 'product',
        'posts_per_page' => 6,
        'post__in'       => $viewed_products,
        'orderby'        => 'post__in',
    );

    $products = new WP_Query($args);

    if ($products->have_posts()) {
        echo '<div class="products-grid">';

        while ($products->have_posts()) {
            $products->the_post();
            wc_get_template_part('content', 'product');
        }

        echo '</div>';
    }

    wp_reset_postdata();

    echo '</section>';
}

Styling

/* Related products custom styling */
.related-products-custom {
    margin: 50px 0;
    padding: 30px 0;
    border-top: 2px solid #e0e0e0;
}

.related-products-custom h2 {
    font-size: 28px;
    margin-bottom: 30px;
    text-align: center;
}

.products-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 30px;
    margin-bottom: 30px;
}

.product-item {
    text-align: center;
    border: 1px solid #e0e0e0;
    padding: 20px;
    border-radius: 8px;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.product-item:hover {
    transform: translateY(-5px);
    box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}

.product-item img {
    width: 100%;
    height: auto;
    margin-bottom: 15px;
    border-radius: 4px;
}

.product-item h3 {
    font-size: 16px;
    margin: 10px 0;
    min-height: 40px;
}

.product-item .price {
    display: block;
    font-size: 18px;
    font-weight: 600;
    color: #0073aa;
    margin: 10px 0;
}

.product-item .button {
    width: 100%;
    margin-top: 10px;
}

/* Recently viewed products */
.recently-viewed-products {
    margin: 50px 0;
    padding: 30px 0;
    background: #f9f9f9;
}

/* Responsive */
@media (max-width: 768px) {
    .products-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 15px;
    }
}

@media (max-width: 480px) {
    .products-grid {
        grid-template-columns: 1fr;
    }
}

Features

  • Flexible Display: Control number of products and columns
  • Custom Algorithm: Select related products by category, tags, price, or brand
  • Manual Selection: Choose specific related products via admin interface
  • Price-Based: Show products in similar price range
  • Brand-Based: Display products from same manufacturer
  • Custom Heading: Change "Related Products" text
  • Custom Position: Move related products section
  • Custom Template: Full control over HTML output
  • Recently Viewed: Track and display recently viewed products
  • Cookie-Based Tracking: Persistent across sessions
  • Responsive Design: Mobile-friendly grid layout
  • Performance: Optimized queries for speed

Dependencies

  • WooCommerce

Related Snippets