parsed_block = $parsed_block; if ( ProductQuery::is_woocommerce_variation( $parsed_block ) && 'woocommerce/related-products' === $parsed_block['attrs']['namespace'] ) { // Set this so that our product filters can detect if it's a PHP template. add_filter( 'query_loop_block_query_vars', array( $this, 'build_query' ), 10, 2 ); } return $pre_render; } /** * Return a custom query based on attributes, filters and global WP_Query. * * @param WP_Query $query The WordPress Query. * @param WP_Block $block The block being rendered. * @return array */ public function build_query( $query, $block = null ) { $parsed_block = $this->parsed_block; if ( ! $this->is_related_products_block( $parsed_block, $block ) ) { return $query; } $related_products_ids = $this->get_related_products_ids( $query['posts_per_page'] ); if ( count( $related_products_ids ) < 1 ) { return array(); } return array( 'post_type' => 'product', 'post__in' => $related_products_ids, 'post_status' => ProductStatus::PUBLISH, 'posts_per_page' => $query['posts_per_page'], ); } /** * If there are no related products, return an empty string. * * @param string $content The block content. * @param array $block The block. * * @return string The block content. */ public function render_block( string $content, array $block ) { if ( ! $this->is_related_products_block( $block ) ) { return $content; } // If there are no related products, render nothing. $related_products_ids = $this->get_related_products_ids(); if ( count( $related_products_ids ) < 1 ) { return ''; } return $content; } /** * Determines whether the block is a related products block. * * @param array $parsed_block The parsed block. * @param array $rendered_block The rendered block. * * @return bool Whether the block is a related products block. */ private function is_related_products_block( $parsed_block, $rendered_block = null ) { $is_product_collection_block = $rendered_block->context['query']['isProductCollectionBlock'] ?? false; if ( ProductQuery::is_woocommerce_variation( $parsed_block ) && isset( $parsed_block['attrs']['namespace'] ) && 'woocommerce/related-products' === $parsed_block['attrs']['namespace'] && ! $is_product_collection_block ) { return true; } return false; } /** * Get related products ids. * The logic is copied from the core function woocommerce_related_products. https://github.com/woocommerce/woocommerce/blob/ca49caabcba84ce9f60a03c6d3534ec14b350b80/plugins/woocommerce/includes/wc-template-functions.php/#L2039-L2074 * * @param number $product_per_page Products per page. * @return array Products ids. */ private function get_related_products_ids( $product_per_page = 5 ) { global $post; $product = wc_get_product( $post->ID ); if ( ! $product instanceof \WC_Product ) { return array(); } $related_products = array_filter( array_map( 'wc_get_product', wc_get_related_products( $product->get_id(), $product_per_page, $product->get_upsell_ids() ) ), 'wc_products_array_filter_visible' ); $related_products = wc_products_array_orderby( $related_products, 'rand', 'desc' ); $related_product_ids = array_map( function ( $product ) { return $product->get_id(); }, $related_products ); return $related_product_ids; } }