Cross-sell items in Product page shortcodes

Snippet 1

/**
 * [product_cross_sells]
 * id, title, limit, columns, class, fallback
 */
add_action('init', function () {
  add_shortcode('product_cross_sells', function ($atts) {
    if (!function_exists('wc_get_product')) return '';

    $a = shortcode_atts([
      'id'       => 0,
      'title'    => 'Abbinamenti consigliati',
      'limit'    => 4,
      'columns'  => 4,
      'class'    => '',
      'fallback' => '', // "related" per mostrare prodotti correlati se non ci sono cross-sell
    ], $atts, 'product_cross_sells');

    // Trova il prodotto
    $prod = null;
    if (!empty($a['id'])) {
      $prod = wc_get_product(absint($a['id']));
    }
    if (!$prod) {
      $qid = get_queried_object_id();
      if ($qid) $prod = wc_get_product($qid);
    }
    if (!$prod) {
      global $product;
      if ($product instanceof WC_Product) $prod = $product;
    }
    if (!$prod) return '';

    $ids = array_values(array_filter(array_map('absint', (array) $prod->get_cross_sell_ids())));

    // Se non ci sono cross-sell e fallback è "related"
    if (empty($ids) && $a['fallback'] === 'related') {
      $related = wc_get_related_products($prod->get_id(), (int)$a['limit']);
      $ids     = array_values(array_filter(array_map('absint', $related)));
    }

    if (empty($ids)) return ''; // niente da mostrare

    $limit   = max(1, (int) $a['limit']);
    $columns = max(1, (int) $a['columns']);
    $ids     = array_slice($ids, 0, $limit);

    $q = new WP_Query([
      'post_type'           => 'product',
      'post__in'            => $ids,
      'orderby'             => 'post__in',
      'posts_per_page'      => $limit,
      'no_found_rows'       => true,
      'ignore_sticky_posts' => true,
      'post_status'         => 'publish',
    ]);
    if (!$q->have_posts()) return '';

    $prev_cols = wc_get_loop_prop('columns');
    wc_set_loop_prop('columns', $columns);

    ob_start();

    $wrapper = 'cross-sells products';
    if (!empty($a['class'])) $wrapper .= ' ' . sanitize_html_class($a['class']);

    echo '<section class="' . esc_attr($wrapper) . '">';
    if ($a['title'] !== '') {
      echo '<h2 class="cross-sells__title">' . esc_html($a['title']) . '</h2>';
    }

    woocommerce_product_loop_start();
    while ($q->have_posts()) { $q->the_post(); wc_get_template_part('content', 'product'); }
    woocommerce_product_loop_end();
    echo '</section>';

    wp_reset_postdata();
    wc_set_loop_prop('columns', $prev_cols);

    return ob_get_clean();
  });
});

Snippet 2

/**
 * Esegui do_shortcode sul contenuto dei blocchi che portano la classe "dm-parse-shortcodes"
 * (utile quando l'area del template non interpreta gli shortcode di suo).
 */
add_filter('render_block', function ($content, $block) {
  if (!is_product()) return $content;
  $attrs = isset($block['attrs']) ? $block['attrs'] : [];
  $class = isset($attrs['className']) ? $attrs['className'] : '';
  if ($class && strpos($class, 'dm-parse-shortcodes') !== false) {
    return do_shortcode($content);
  }
  return $content;
}, 10, 2);